diff --git a/README.md b/README.md
index 778f7696..2bba41ed 100644
--- a/README.md
+++ b/README.md
@@ -42,12 +42,14 @@ Example usage in your `pom.xml`:
my.package.path.model
-
+
my.package.path.client
my.package.path.model
- simple
-
+
+ true
+
+
@@ -78,11 +80,13 @@ asyncapiGenerate {
packageName.set("my.package.path.model")
}
clients {
- springKafka {
+ kafka {
packageName.set("my.package.path.client")
// Optional: defaults to models.packageName when models are configured
modelPackageName.set("my.package.path.model")
- mode.set("simple") // options: full, simple - default simple
+ springKafka {
+ enabled.set(true)
+ }
}
}
schemas {
@@ -111,11 +115,13 @@ asyncapiGenerate {
packageName = 'my.package.path.model'
}
clients {
- springKafka {
+ kafka {
packageName = 'my.package.path.client'
// Optional: defaults to models.packageName when models are configured
modelPackageName = 'my.package.path.model'
- mode = 'simple' // options: full, simple - default simple
+ springKafka {
+ enabled = true
+ }
}
}
schemas {
@@ -301,24 +307,21 @@ Generated Java Protobuf message sources are produced by running `protoc` during
### Spring Kafka Clients
-Spring Kafka output is configured under `clients.springKafka`.
+Spring Kafka output is configured under `clients.kafka.springKafka`.
-Generated Spring Kafka clients use `models.packageName` for payload model types by default. If models are generated elsewhere, configure `clients.springKafka.modelPackageName` to point the client API at that package without generating model output in the same execution.
+Generated Spring Kafka clients use `models.packageName` for payload model types by default. If models are generated elsewhere, configure `clients.kafka.modelPackageName` to point the client API at that package without generating model output in the same execution.
-For native Avro message payloads, generated Spring Kafka clients use the Java type declared by the Avro schema namespace and name. For example, a native Avro schema with `namespace: com.example.avro` and `name: UserCreated` is used as `com.example.avro.UserCreated` in generated producer, consumer, listener, and handler APIs.
+Kafka client configuration can also be narrowed by capability. `clients.kafka.headers.enabled` controls typed header model generation, and `clients.kafka.springKafka.producer.enabled` / `clients.kafka.springKafka.consumer.enabled` control whether producer and consumer artifacts are generated.
+
+For native Avro message payloads, generated Spring Kafka clients use the Java type declared by the Avro schema namespace and name. For example, a native Avro schema with `namespace: com.example.avro` and `name: UserCreated` is used as `com.example.avro.UserCreated` in generated producer and consumer APIs.
For native Protobuf message payloads, generated Spring Kafka clients use the Java type declared by `option java_package`, or by the Protobuf `package` when `java_package` is omitted. Protobuf client generation requires `option java_multiple_files = true;` so the generated message can be referenced as a top-level Java type. The `.proto` schema must contain a top-level message matching the payload name.
The generator does not configure Kafka Avro or Protobuf serializers and deserializers yet; applications still own that runtime wiring.
-The current generator has two modes:
-
-- `mode = "full"` generates Spring Boot-oriented client artifacts. This includes producer classes, listener classes, handler interfaces, an auto-configuration class, and the Spring Boot auto-configuration import resource. Generated producers and listeners use topic property keys, for example `kafka.topics.customerUpdated`, instead of hard-coding topic names directly in the generated source.
-- `mode = "simple"` generates lightweight producer and consumer source artifacts without Spring Boot auto-configuration. The application owns how those generated types are instantiated and connected to Spring Kafka infrastructure.
-
-When `mode` is omitted, the generator uses `simple`.
+Generated Spring Kafka clients are contract-only source artifacts. Producer-oriented channels generate producer wrappers around application-provided `KafkaTemplate` instances and return Spring Kafka `CompletableFuture>` values from send methods. Consumer-oriented channels generate consumer interfaces with abstract methods that receive typed `ConsumerRecord` values. The generator does not create Spring Boot auto-configuration, `@KafkaListener` classes, listener containers, serializer configuration, deserializer configuration, or schema registry configuration.
-The generated output depends on the channel direction from the AsyncAPI operations. Producer-oriented channels generate producer artifacts. Consumer-oriented channels generate consumer, listener, or handler artifacts depending on the selected mode. When the channel direction is not declared, the generator treats the channel as both producer and consumer.
+The generated output depends on the channel direction from the AsyncAPI operations. Producer-oriented channels generate producer artifacts. Consumer-oriented channels generate consumer artifacts. When the channel direction is not declared, the generator treats the channel as both producer and consumer.
The Spring Kafka client surface is still being redesigned for the next major version. The generated artifacts should currently be treated as a source-generation contract, not as final application architecture guidance.
diff --git a/asyncapi-generator-cli/src/main/kotlin/dev/banking/asyncapi/generator/cli/Main.kt b/asyncapi-generator-cli/src/main/kotlin/dev/banking/asyncapi/generator/cli/Main.kt
index 9cce9227..f2192e7f 100644
--- a/asyncapi-generator-cli/src/main/kotlin/dev/banking/asyncapi/generator/cli/Main.kt
+++ b/asyncapi-generator-cli/src/main/kotlin/dev/banking/asyncapi/generator/cli/Main.kt
@@ -16,7 +16,6 @@ import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorConf
import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorConfigurationRequest
import dev.banking.asyncapi.generator.core.generator.configuration.JavaModelType
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import dev.banking.asyncapi.generator.core.parser.AsyncApiParser
import dev.banking.asyncapi.generator.core.registry.AsyncApiRegistry
import dev.banking.asyncapi.generator.core.validator.AsyncApiValidator
@@ -94,32 +93,48 @@ class AsyncApiGeneratorCli : CliktCommand(name = "asyncapi-generator") {
"false" to false,
)
- private val clientsSpringKafka by option(
- "--clients-spring-kafka",
- help = "Enable Spring Kafka client generation",
+ private val clientsKafka by option(
+ "--clients-kafka",
+ help = "Enable Kafka client generation",
).flag(default = false)
- private val clientsSpringKafkaPackage by option(
- "--clients-spring-kafka-package",
- help = "Package for generated Spring Kafka clients",
+ private val clientsKafkaPackage by option(
+ "--clients-kafka-package",
+ help = "Package for generated Kafka clients",
)
- private val clientsSpringKafkaModelPackage by option(
- "--clients-spring-kafka-model-package",
- help = "Package containing model types used by generated Spring Kafka clients",
+ private val clientsKafkaModelPackage by option(
+ "--clients-kafka-model-package",
+ help = "Package containing model types used by generated Kafka clients",
)
- private val clientsSpringKafkaMode by option(
- "--clients-spring-kafka-mode",
- help = "Spring Kafka generation mode (default: simple)",
+ private val clientsKafkaHeaders by option(
+ "--clients-kafka-headers",
+ help = "Generate typed Kafka header models when headers are defined (default: true)",
).choice(
- SpringKafkaClientType.FULL.configurationValue to SpringKafkaClientType.FULL,
- SpringKafkaClientType.SIMPLE.configurationValue to SpringKafkaClientType.SIMPLE,
+ "true" to true,
+ "false" to false,
+ )
+
+ private val clientsKafkaSpringKafka by option(
+ "--clients-kafka-spring-kafka",
+ help = "Enable Spring Kafka client generation under Kafka clients",
+ ).flag(default = false)
+
+ private val clientsKafkaSpringKafkaProducer by option(
+ "--clients-kafka-spring-kafka-producer",
+ help = "Generate Spring Kafka producer APIs (default: true)",
+ ).choice(
+ "true" to true,
+ "false" to false,
)
- private val clientsSpringKafkaTopicPropertyPrefix by option(
- "--clients-spring-kafka-topic-property-prefix",
- help = "Spring Kafka topic property prefix (default: kafka.topics)",
+ private val clientsKafkaSpringKafkaConsumer by option(
+ "--clients-kafka-spring-kafka-consumer",
+ help = "Generate Spring Kafka consumer APIs (default: true)",
+ ).choice(
+ "true" to true,
+ "false" to false,
)
private val clientsQuarkusKafka by option(
@@ -220,13 +235,24 @@ class AsyncApiGeneratorCli : CliktCommand(name = "asyncapi-generator") {
private fun clientRequest(): GeneratorConfigurationRequest.Clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.springKafka(
- enabled = true.takeIf { clientsSpringKafka },
- packageName = clientsSpringKafkaPackage,
- modelPackageName = clientsSpringKafkaModelPackage,
- mode = clientsSpringKafkaMode?.configurationValue,
- topicPropertyPrefix = clientsSpringKafkaTopicPropertyPrefix,
+ kafka =
+ GeneratorConfigurationRequest.kafka(
+ enabled = true.takeIf { clientsKafka },
+ packageName = clientsKafkaPackage,
+ modelPackageName = clientsKafkaModelPackage,
+ headers = GeneratorConfigurationRequest.kafkaHeaders(enabled = clientsKafkaHeaders),
+ springKafka =
+ GeneratorConfigurationRequest.kafkaSpringKafka(
+ enabled = true.takeIf { clientsKafkaSpringKafka },
+ producer =
+ GeneratorConfigurationRequest.kafkaProducer(
+ enabled = clientsKafkaSpringKafkaProducer,
+ ),
+ consumer =
+ GeneratorConfigurationRequest.kafkaConsumer(
+ enabled = clientsKafkaSpringKafkaConsumer,
+ ),
+ ),
),
quarkusKafka =
GeneratorConfigurationRequest.quarkusKafka(
diff --git a/asyncapi-generator-cli/src/test/kotlin/dev/banking/asyncapi/generator/cli/AsyncApiGeneratorCliTest.kt b/asyncapi-generator-cli/src/test/kotlin/dev/banking/asyncapi/generator/cli/AsyncApiGeneratorCliTest.kt
index ff226124..815137e1 100644
--- a/asyncapi-generator-cli/src/test/kotlin/dev/banking/asyncapi/generator/cli/AsyncApiGeneratorCliTest.kt
+++ b/asyncapi-generator-cli/src/test/kotlin/dev/banking/asyncapi/generator/cli/AsyncApiGeneratorCliTest.kt
@@ -26,9 +26,9 @@ class AsyncApiGeneratorCliTest {
"--codegen-output", codegenDir.absolutePath,
"--resource-output", resourceDir.absolutePath,
"--models-package", "com.example.cli.model",
- "--clients-spring-kafka-package", "com.example.cli.client",
+ "--clients-kafka-package", "com.example.cli.client",
+ "--clients-kafka-spring-kafka",
"--generator", "kotlin",
- "--clients-spring-kafka-mode", "full",
)
)
val packageDir = codegenDir.resolve("src/main/kotlin/com/example/cli/client")
@@ -46,8 +46,9 @@ class AsyncApiGeneratorCliTest {
"--input", inputFile.absolutePath,
"--codegen-output", codegenDir.absolutePath,
"--resource-output", resourceDir.absolutePath,
- "--clients-spring-kafka-package", "com.example.cli.client",
- "--clients-spring-kafka-model-package", "com.example.cli.model",
+ "--clients-kafka-package", "com.example.cli.client",
+ "--clients-kafka-model-package", "com.example.cli.model",
+ "--clients-kafka-spring-kafka",
"--generator", "kotlin",
)
)
@@ -196,7 +197,7 @@ class AsyncApiGeneratorCliTest {
}
@Test
- fun `should fail if spring kafka client is enabled without client package`(@TempDir tempDir: Path) {
+ fun `should fail if kafka spring kafka client is enabled without client package`(@TempDir tempDir: Path) {
val inputFile = File("src/test/resources/asyncapi_kafka_complex.yaml")
val codegenDir = tempDir.resolve("codegen").toFile()
val exception =
@@ -206,14 +207,14 @@ class AsyncApiGeneratorCliTest {
"-i", inputFile.absolutePath,
"--codegen-output", codegenDir.absolutePath,
"--models-package", "com.example.cli.model",
- "--clients-spring-kafka",
+ "--clients-kafka-spring-kafka",
)
)
}
assertTrue(
exception.message.orEmpty().contains(
- "clients.springKafka.packageName is required when clients.springKafka is configured",
+ "clients.kafka.packageName is required when clients.kafka is configured",
),
)
}
@@ -263,24 +264,6 @@ class AsyncApiGeneratorCliTest {
)
}
- @Test
- fun `should fail if spring kafka mode is invalid`(@TempDir tempDir: Path) {
- val inputFile = File("src/test/resources/asyncapi_kafka_complex.yaml")
- val codegenDir = tempDir.resolve("codegen").toFile()
- assertFailsWith {
- cli.parse(
- arrayOf(
- "-i", inputFile.absolutePath,
- "--codegen-output", codegenDir.absolutePath,
- "--models-package", "com.example.cli.model",
- "-g", "kotlin",
- "--clients-spring-kafka-package", "com.example.cli.client",
- "--clients-spring-kafka-mode", "basic",
- )
- )
- }
- }
-
@Test
fun `should fail if java model type is invalid`(@TempDir tempDir: Path) {
val inputFile = File("src/test/resources/asyncapi_kafka_complex.yaml")
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessage.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessage.kt
index 4c4c17f6..94e17da4 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessage.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessage.kt
@@ -7,4 +7,5 @@ data class AnalyzedMessage(
val payloadTypeName: String, // The payload type name (e.g. "UserSignedUpPayload")
val schema: Schema, // The payload schema
val keySchema: Schema? = null, // Optional Kafka Key schema
+ val headers: AnalyzedMessageHeaders? = null,
)
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessageHeaders.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessageHeaders.kt
new file mode 100644
index 00000000..b0f90d7a
--- /dev/null
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMessageHeaders.kt
@@ -0,0 +1,8 @@
+package dev.banking.asyncapi.generator.core.generator.analyzer
+
+import dev.banking.asyncapi.generator.core.model.schemas.SchemaInterface
+
+data class AnalyzedMessageHeaders(
+ val typeName: String,
+ val properties: Map,
+)
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMultiFormatMessage.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMultiFormatMessage.kt
index 9b43dc08..bceeb990 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMultiFormatMessage.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/AnalyzedMultiFormatMessage.kt
@@ -15,4 +15,5 @@ data class AnalyzedMultiFormatMessage(
val messageName: String,
val payloadName: String,
val schema: MultiFormatSchema,
+ val headers: AnalyzedMessageHeaders? = null,
)
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzer.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzer.kt
index 9c1c9c08..be26ee29 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzer.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzer.kt
@@ -63,7 +63,7 @@ class ChannelAnalyzer {
val usage = channelUsage[name]!!
val finalProducer = if (!usage.isProducer && !usage.isConsumer) true else usage.isProducer
val finalConsumer = if (!usage.isProducer && !usage.isConsumer) true else usage.isConsumer
- val resolvedMessages = resolveMessages(channel.messages)
+ val resolvedMessages = resolveMessages(channelName = name, messages = channel.messages)
AnalyzedChannel(
channelName = name,
@@ -78,7 +78,10 @@ class ChannelAnalyzer {
return ChannelAnalysisResult(analyzedChannels)
}
- private fun resolveMessages(messages: Map?): ResolvedMessages {
+ private fun resolveMessages(
+ channelName: String,
+ messages: Map?,
+ ): ResolvedMessages {
if (messages.isNullOrEmpty()) return ResolvedMessages()
val analyzedMessages = mutableListOf()
val analyzedMultiFormatMessages = mutableListOf()
@@ -95,6 +98,12 @@ class ChannelAnalyzer {
var typeName: String? = null
val baseName = MapperUtil.toPascalCase(message.name ?: message.title ?: name)
val inlinePayloadTypeName = if (baseName.endsWith("Payload")) baseName else "${baseName}Payload"
+ val headers =
+ MessageHeaderAnalyzer.analyze(
+ channelName = channelName,
+ messageKey = name,
+ message = message,
+ )
when (val p = message.payload) {
is SchemaInterface.SchemaInline -> {
@@ -123,6 +132,7 @@ class ChannelAnalyzer {
messageName = baseName,
payloadTypeName = typeName,
schema = payloadSchema,
+ headers = headers,
),
)
} else if (multiFormatSchema != null) {
@@ -131,6 +141,7 @@ class ChannelAnalyzer {
messageName = baseName,
payloadName = typeName,
schema = multiFormatSchema,
+ headers = headers,
),
)
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/MessageHeaderAnalyzer.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/MessageHeaderAnalyzer.kt
new file mode 100644
index 00000000..c5dada80
--- /dev/null
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/MessageHeaderAnalyzer.kt
@@ -0,0 +1,73 @@
+package dev.banking.asyncapi.generator.core.generator.analyzer
+
+import dev.banking.asyncapi.generator.core.generator.util.MapperUtil
+import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryType
+import dev.banking.asyncapi.generator.core.model.messages.Message
+import dev.banking.asyncapi.generator.core.model.messages.MessageTrait
+import dev.banking.asyncapi.generator.core.model.messages.MessageTraitInterface
+import dev.banking.asyncapi.generator.core.model.schemas.Schema
+import dev.banking.asyncapi.generator.core.model.schemas.SchemaInterface
+
+/**
+ * Resolves generated header DTO metadata from AsyncAPI message headers.
+ *
+ * Expected behavior is covered by:
+ * - `ChannelAnalyzerTest`
+ * - `JavaModelPreparerTest`
+ * - `KotlinModelPreparerTest`
+ */
+object MessageHeaderAnalyzer {
+ fun analyze(
+ channelName: String,
+ messageKey: String,
+ message: Message,
+ ): AnalyzedMessageHeaders? {
+ val properties = collectProperties(message)
+ if (properties.isEmpty()) return null
+
+ val channelNamePascal = MapperUtil.toPascalCase(channelName)
+ val messageName = message.name ?: message.title ?: messageKey
+ val messageNamePascal = MapperUtil.toPascalCase(messageName)
+
+ return AnalyzedMessageHeaders(
+ typeName = "Topic${channelNamePascal}Headers$messageNamePascal",
+ properties = properties,
+ )
+ }
+
+ private fun collectProperties(message: Message): Map {
+ val headers = mutableMapOf()
+
+ message.traits?.forEach { traitInterface ->
+ val trait =
+ when (traitInterface) {
+ is MessageTraitInterface.InlineMessageTrait -> traitInterface.trait
+ is MessageTraitInterface.ReferenceMessageTrait -> traitInterface.reference.model as? MessageTrait
+ }
+ trait?.headers?.let { headers.putAll(extractProperties(it)) }
+ }
+
+ message.headers?.let { headers.putAll(extractProperties(it)) }
+
+ return headers
+ }
+
+ private fun extractProperties(schemaInterface: SchemaInterface): Map =
+ when (schemaInterface) {
+ is SchemaInterface.SchemaInline ->
+ if (schemaInterface.schema.type.getPrimaryType() == "object") {
+ schemaInterface.schema.properties ?: emptyMap()
+ } else {
+ emptyMap()
+ }
+ is SchemaInterface.SchemaReference -> {
+ val schema = schemaInterface.reference.model as? Schema
+ if (schema?.type.getPrimaryType() == "object") {
+ schema?.properties ?: emptyMap()
+ } else {
+ emptyMap()
+ }
+ }
+ else -> emptyMap()
+ }
+}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/ClientGeneration.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/ClientGeneration.kt
index 93f3fb4d..2c9a0621 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/ClientGeneration.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/ClientGeneration.kt
@@ -1,7 +1,5 @@
package dev.banking.asyncapi.generator.core.generator.configuration
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
-
/**
* Typed client generation capabilities requested by generator configuration.
*
@@ -9,13 +7,30 @@ import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
* - `GenerationPlannerTest`
*/
sealed interface ClientGeneration {
- data class SpringKafka(
+ data class Kafka(
val packageName: String,
val modelPackageName: String,
- val clientType: SpringKafkaClientType = SpringKafkaClientType.SIMPLE,
- val topicPropertyPrefix: String = "kafka.topics",
+ val headers: Headers = Headers(),
+ val springKafka: SpringKafka? = null,
) : ClientGeneration
+ data class Headers(
+ val enabled: Boolean = true,
+ )
+
+ data class SpringKafka(
+ val producer: Producer = Producer(),
+ val consumer: Consumer = Consumer(),
+ )
+
+ data class Producer(
+ val enabled: Boolean = true,
+ )
+
+ data class Consumer(
+ val enabled: Boolean = true,
+ )
+
data class QuarkusKafka(
val packageName: String,
val modelPackageName: String,
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactory.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactory.kt
index f017467f..2df14ddd 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactory.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactory.kt
@@ -52,20 +52,35 @@ object GeneratorConfigurationFactory {
},
clients =
buildList {
- request.clients.springKafka?.let { springKafka ->
+ request.clients.kafka?.let { kafka ->
add(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = requiredPackageName(
- path = "clients.springKafka.packageName",
- value = springKafka.packageName,
+ path = "clients.kafka.packageName",
+ value = kafka.packageName,
),
modelPackageName = requiredClientModelPackageName(
- path = "clients.springKafka.modelPackageName",
- configuredModelPackageName = springKafka.modelPackageName,
+ path = "clients.kafka.modelPackageName",
+ configuredModelPackageName = kafka.modelPackageName,
modelsPackageName = request.models?.packageName,
),
- clientType = springKafka.clientType,
- topicPropertyPrefix = springKafka.topicPropertyPrefix,
+ headers =
+ ClientGeneration.Headers(
+ enabled = kafka.headers.enabled,
+ ),
+ springKafka =
+ kafka.springKafka?.let { springKafka ->
+ ClientGeneration.SpringKafka(
+ producer =
+ ClientGeneration.Producer(
+ enabled = springKafka.producer.enabled,
+ ),
+ consumer =
+ ClientGeneration.Consumer(
+ enabled = springKafka.consumer.enabled,
+ ),
+ )
+ },
),
)
}
@@ -108,9 +123,9 @@ object GeneratorConfigurationFactory {
)
}
- if (request.clients.springKafka != null && request.clients.springKafka.packageName == null) {
+ if (request.clients.kafka != null && request.clients.kafka.packageName == null) {
throw IllegalArgumentException(
- "clients.springKafka.packageName is required when clients.springKafka is configured",
+ "clients.kafka.packageName is required when clients.kafka is configured",
)
}
@@ -120,10 +135,6 @@ object GeneratorConfigurationFactory {
)
}
- if (request.clients.springKafka?.topicPropertyPrefix?.isBlank() == true) {
- throw IllegalArgumentException("clients.springKafka.topicPropertyPrefix cannot be empty")
- }
-
validatePackageName(
path = "models.packageName",
value = request.models?.packageName,
@@ -133,12 +144,12 @@ object GeneratorConfigurationFactory {
value = request.schemas.avroProjection?.packageName,
)
validatePackageName(
- path = "clients.springKafka.packageName",
- value = request.clients.springKafka?.packageName,
+ path = "clients.kafka.packageName",
+ value = request.clients.kafka?.packageName,
)
validatePackageName(
- path = "clients.springKafka.modelPackageName",
- value = request.clients.springKafka?.modelPackageName,
+ path = "clients.kafka.modelPackageName",
+ value = request.clients.kafka?.modelPackageName,
)
validatePackageName(
path = "clients.quarkusKafka.packageName",
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequest.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequest.kt
index dc43f8e9..e3b9d8d5 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequest.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequest.kt
@@ -1,7 +1,6 @@
package dev.banking.asyncapi.generator.core.generator.configuration
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import java.io.File
/**
@@ -48,15 +47,32 @@ data class GeneratorConfigurationRequest(
)
data class Clients(
- val springKafka: SpringKafka? = null,
+ val kafka: Kafka? = null,
val quarkusKafka: QuarkusKafka? = null,
)
- data class SpringKafka(
+ data class Kafka(
val packageName: String? = null,
val modelPackageName: String? = null,
- val clientType: SpringKafkaClientType = SpringKafkaClientType.SIMPLE,
- val topicPropertyPrefix: String = DEFAULT_KAFKA_TOPICS_PROPERTY_PREFIX,
+ val headers: KafkaHeaders = KafkaHeaders(),
+ val springKafka: KafkaSpringKafka? = null,
+ )
+
+ data class KafkaHeaders(
+ val enabled: Boolean = true,
+ )
+
+ data class KafkaSpringKafka(
+ val producer: KafkaProducer = KafkaProducer(),
+ val consumer: KafkaConsumer = KafkaConsumer(),
+ )
+
+ data class KafkaProducer(
+ val enabled: Boolean = true,
+ )
+
+ data class KafkaConsumer(
+ val enabled: Boolean = true,
)
data class QuarkusKafka(
@@ -65,8 +81,6 @@ data class GeneratorConfigurationRequest(
)
companion object {
- const val DEFAULT_KAFKA_TOPICS_PROPERTY_PREFIX = "kafka.topics"
-
fun models(
enabled: Boolean? = null,
packageName: String? = null,
@@ -121,33 +135,62 @@ data class GeneratorConfigurationRequest(
else -> null
}
- fun springKafka(
+ fun kafka(
enabled: Boolean? = null,
packageName: String? = null,
modelPackageName: String? = null,
- mode: String? = null,
- topicPropertyPrefix: String? = null,
- ): SpringKafka? =
+ headers: KafkaHeaders? = null,
+ springKafka: KafkaSpringKafka? = null,
+ ): Kafka? =
when {
enabled == false -> null
enabled == true ||
packageName != null ||
modelPackageName != null ||
- mode != null ||
- topicPropertyPrefix != null ->
- SpringKafka(
+ headers != null ||
+ springKafka != null ->
+ Kafka(
packageName = packageName,
modelPackageName = modelPackageName,
- clientType =
- SpringKafkaClientType.fromConfigurationValue(
- value = mode,
- path = "clients.springKafka.mode",
- ),
- topicPropertyPrefix = topicPropertyPrefix ?: DEFAULT_KAFKA_TOPICS_PROPERTY_PREFIX,
+ headers = headers ?: KafkaHeaders(),
+ springKafka = springKafka,
)
else -> null
}
+ fun kafkaHeaders(enabled: Boolean? = null): KafkaHeaders? =
+ when (enabled) {
+ null -> null
+ else -> KafkaHeaders(enabled = enabled)
+ }
+
+ fun kafkaSpringKafka(
+ enabled: Boolean? = null,
+ producer: KafkaProducer? = null,
+ consumer: KafkaConsumer? = null,
+ ): KafkaSpringKafka? =
+ when {
+ enabled == false -> null
+ enabled == true || producer != null || consumer != null ->
+ KafkaSpringKafka(
+ producer = producer ?: KafkaProducer(),
+ consumer = consumer ?: KafkaConsumer(),
+ )
+ else -> null
+ }
+
+ fun kafkaProducer(enabled: Boolean? = null): KafkaProducer? =
+ when (enabled) {
+ null -> null
+ else -> KafkaProducer(enabled = enabled)
+ }
+
+ fun kafkaConsumer(enabled: Boolean? = null): KafkaConsumer? =
+ when (enabled) {
+ null -> null
+ else -> KafkaConsumer(enabled = enabled)
+ }
+
fun quarkusKafka(
enabled: Boolean? = null,
packageName: String? = null,
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaKafkaGeneratorModelFactory.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaKafkaGeneratorModelFactory.kt
deleted file mode 100644
index b5427f72..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaKafkaGeneratorModelFactory.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.factory
-
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessage
-import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaPayload
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.NativeKafkaPayloadResolver
-import dev.banking.asyncapi.generator.core.generator.util.DocumentationUtils
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryType
-import dev.banking.asyncapi.generator.core.model.schemas.Schema
-import dev.banking.asyncapi.generator.core.model.schemas.SchemaInterface
-
-class JavaKafkaGeneratorModelFactory(
- private val packageName: String,
- private val modelPackage: String,
- private val topicPropertyPrefix: String,
- private val nativeKafkaPayloadResolver: NativeKafkaPayloadResolver = NativeKafkaPayloadResolver(),
-) {
- fun create(channel: AnalyzedChannel): List {
- val items = mutableListOf()
- val baseName = MapperUtil.toPascalCase(channel.channelName)
- val handlerPackage = "$packageName.handler"
- val listenerPackage = "$packageName.listener"
- val producerPackage = "$packageName.producer"
- val payloads = channel.payloads()
-
- val baseImports =
- payloads.mapNotNull { payload -> payload.importName }
- val imports =
- (baseImports + "org.apache.kafka.clients.consumer.ConsumerRecord")
- .distinct()
- .sorted()
-
- if (channel.isConsumer) {
- val topicPropertyKey = topicPropertyKey(channel.channelName)
- val topicPrefix = "Topic$baseName"
- payloads.forEach { payload ->
- val methodName = "on${payload.messageName}"
- val handlerName = "${topicPrefix}Handler${payload.messageName}"
- items.add(
- GeneratorItem.KafkaHandlerInterface(
- name = handlerName,
- packageName = handlerPackage,
- description = DocumentationUtils.toJavaDocLines("Handler for messages on topic '${channel.topic}'"),
- methods =
- listOf(
- GeneratorItem.HandlerMethod(
- methodName = methodName,
- payloadType = payload.payloadType,
- ),
- ),
- imports = imports,
- ),
- )
- val listenerName = "${topicPrefix}Listener${payload.messageName}"
- val listenerImports = (imports + "$handlerPackage.$handlerName").distinct().sorted()
- items.add(
- GeneratorItem.KafkaListenerClass(
- name = listenerName,
- packageName = listenerPackage,
- description = DocumentationUtils.toJavaDocLines("Spring Kafka Listener for topic '${channel.topic}'"),
- topic = channel.topic,
- groupId = "\${spring.kafka.consumer.group-id}",
- handlerInterface = handlerName,
- payloadType = payload.payloadType,
- methodName = methodName,
- imports = listenerImports,
- topicPropertyKey = topicPropertyKey,
- ),
- )
- }
- }
-
- if (channel.isProducer) {
- val topicPropertyKey = topicPropertyKey(channel.channelName)
- val topicPrefix = "Topic$baseName"
- payloads.forEach { payload ->
- val sendMethod =
- GeneratorItem.SendMethod(
- methodName = "send${payload.messageName}",
- payloadType = payload.payloadType,
- )
- val producerName = "${topicPrefix}Producer${payload.messageName}"
- items.add(
- GeneratorItem.KafkaProducerClass(
- name = producerName,
- packageName = producerPackage,
- description = DocumentationUtils.toJavaDocLines("Producer for topic '${channel.topic}'"),
- topic = channel.topic,
- sendMethods = listOf(sendMethod),
- kafkaValueType = payload.payloadType,
- imports = imports,
- topicPropertyKey = topicPropertyKey,
- ),
- )
- }
- }
- return items
- }
-
- private fun topicPropertyKey(channelName: String): String = "$topicPropertyPrefix.$channelName"
-
- private fun AnalyzedChannel.payloads(): List =
- messages.map(::payload) + multiFormatMessages.mapNotNull(nativeKafkaPayloadResolver::resolve)
-
- private fun payload(msg: AnalyzedMessage): KafkaPayload {
- val type = resolvePayloadType(msg)
- return KafkaPayload(
- messageName = msg.messageName,
- payloadType = type,
- importName =
- if (isPrimitive(type)) {
- null
- } else {
- "$modelPackage.$type"
- },
- )
- }
-
- private fun resolvePayloadType(msg: AnalyzedMessage): String =
- if (isOpenPayloadSchema(msg.schema)) {
- "Object"
- } else {
- when (msg.schema.type.getPrimaryType()) {
- "string" -> "String"
- "integer" -> "Integer"
- "number" -> "java.math.BigDecimal"
- "boolean" -> "Boolean"
- else -> msg.payloadTypeName
- }
- }
-
- private fun isOpenPayloadSchema(schema: Schema): Boolean {
- if (schema.type == null) {
- return schema.properties.isNullOrEmpty() &&
- schema.additionalProperties == null &&
- schema.enum.isNullOrEmpty() &&
- schema.oneOf.isNullOrEmpty() &&
- schema.anyOf.isNullOrEmpty() &&
- schema.allOf.isNullOrEmpty()
- }
- if (schema.type.getPrimaryType() != "object") return false
- if (!schema.properties.isNullOrEmpty()) return false
- return when (val additional = schema.additionalProperties) {
- null -> true
- is SchemaInterface.BooleanSchema -> additional.value
- is SchemaInterface.SchemaInline ->
- additional.schema.type == null &&
- additional.schema.properties.isNullOrEmpty() &&
- additional.schema.additionalProperties == null
- else -> false
- }
- }
-
- private fun isPrimitive(type: String): Boolean =
- type in setOf("String", "Integer", "Long", "Boolean", "Double", "java.math.BigDecimal", "Object")
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaSimpleModelFactory.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaModelFactory.kt
similarity index 61%
rename from asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaSimpleModelFactory.kt
rename to asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaModelFactory.kt
index 9f6bafd2..bdd0edab 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaSimpleModelFactory.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/factory/JavaSpringKafkaModelFactory.kt
@@ -2,6 +2,8 @@ package dev.banking.asyncapi.generator.core.generator.java.factory
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessage
+import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessageHeaders
+import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaHeaderProperty
import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaPayload
import dev.banking.asyncapi.generator.core.generator.kafka.spring.NativeKafkaPayloadResolver
@@ -11,9 +13,12 @@ import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryT
import dev.banking.asyncapi.generator.core.model.schemas.Schema
import dev.banking.asyncapi.generator.core.model.schemas.SchemaInterface
-class JavaSpringKafkaSimpleModelFactory(
+class JavaSpringKafkaModelFactory(
private val clientPackage: String,
private val modelPackage: String,
+ private val generateHeaders: Boolean = true,
+ private val generateProducers: Boolean = true,
+ private val generateConsumers: Boolean = true,
private val nativeKafkaPayloadResolver: NativeKafkaPayloadResolver = NativeKafkaPayloadResolver(),
) {
fun create(channel: AnalyzedChannel): List {
@@ -24,9 +29,11 @@ class JavaSpringKafkaSimpleModelFactory(
val payloads = channel.payloads()
val baseImports =
- payloads.mapNotNull { payload -> payload.importName }
+ payloads.flatMap { payload -> listOfNotNull(payload.importName, payload.headerImportName) }
+ .distinct()
+ .sorted()
- if (channel.isConsumer) {
+ if (channel.isConsumer && generateConsumers) {
val consumerName = "${baseName}Consumer"
val imports = (baseImports + "org.apache.kafka.clients.consumer.ConsumerRecord").distinct().sorted()
val methods =
@@ -34,6 +41,7 @@ class JavaSpringKafkaSimpleModelFactory(
GeneratorItem.HandlerMethod(
methodName = "on${payload.messageName}",
payloadType = payload.payloadType,
+ headerType = payload.headerTypeName,
)
}
items.add(
@@ -47,9 +55,16 @@ class JavaSpringKafkaSimpleModelFactory(
)
}
- if (channel.isProducer) {
+ if (channel.isProducer && generateProducers) {
val imports =
- (baseImports + "org.apache.kafka.clients.producer.ProducerRecord" + "org.springframework.kafka.core.KafkaTemplate")
+ (
+ baseImports +
+ "java.util.concurrent.CompletableFuture" +
+ "org.apache.kafka.clients.producer.ProducerRecord" +
+ "org.springframework.kafka.core.KafkaTemplate" +
+ "org.springframework.kafka.support.SendResult" +
+ listOfNotNull("java.nio.charset.StandardCharsets".takeIf { payloads.any { it.headerProperties.isNotEmpty() } })
+ )
.distinct()
.sorted()
payloads.forEach { payload ->
@@ -57,6 +72,14 @@ class JavaSpringKafkaSimpleModelFactory(
GeneratorItem.SendMethod(
methodName = "send${payload.messageName}",
payloadType = payload.payloadType,
+ headerType = payload.headerTypeName,
+ headerProperties =
+ payload.headerProperties.map { header ->
+ GeneratorItem.HeaderProperty(
+ name = header.name,
+ accessorName = header.accessorName,
+ )
+ },
)
val producerName = "${baseName}Producer${payload.messageName}"
items.add(
@@ -68,7 +91,6 @@ class JavaSpringKafkaSimpleModelFactory(
sendMethods = listOf(sendMethod),
kafkaValueType = payload.payloadType,
imports = imports,
- topicPropertyKey = "",
),
)
}
@@ -78,10 +100,15 @@ class JavaSpringKafkaSimpleModelFactory(
}
private fun AnalyzedChannel.payloads(): List =
- messages.map(::payload) + multiFormatMessages.mapNotNull(nativeKafkaPayloadResolver::resolve)
+ messages.map(::payload) +
+ multiFormatMessages.mapNotNull { message ->
+ nativeKafkaPayloadResolver.resolve(message)
+ ?.withHeaders(message.headers)
+ }
private fun payload(msg: AnalyzedMessage): KafkaPayload {
val type = resolvePayloadType(msg)
+ val headers = if (generateHeaders) msg.headers else null
return KafkaPayload(
messageName = msg.messageName,
payloadType = type,
@@ -91,6 +118,19 @@ class JavaSpringKafkaSimpleModelFactory(
} else {
"$modelPackage.$type"
},
+ headerTypeName = headers?.typeName,
+ headerImportName = headers?.typeName?.let { "$clientPackage.header.$it" },
+ headerProperties =
+ headers
+ ?.properties
+ ?.keys
+ ?.map { headerName ->
+ KafkaHeaderProperty(
+ name = headerName,
+ accessorName = getterName(headerName),
+ )
+ }
+ .orEmpty(),
)
}
@@ -131,4 +171,28 @@ class JavaSpringKafkaSimpleModelFactory(
private fun isPrimitive(type: String): Boolean =
type in setOf("String", "Integer", "Long", "Boolean", "Double", "java.math.BigDecimal", "Object")
+
+ private fun KafkaPayload.withHeaders(headers: AnalyzedMessageHeaders?): KafkaPayload =
+ if (generateHeaders) {
+ copy(
+ headerTypeName = headers?.typeName,
+ headerImportName = headers?.typeName?.let { "$clientPackage.header.$it" },
+ headerProperties =
+ headers
+ ?.properties
+ ?.keys
+ ?.map { headerName ->
+ KafkaHeaderProperty(
+ name = headerName,
+ accessorName = getterName(headerName),
+ )
+ }
+ .orEmpty(),
+ )
+ } else {
+ this
+ }
+
+ private fun getterName(propertyName: String): String =
+ "get" + propertyName.replaceFirstChar { it.uppercase() }
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaAutoConfigurationGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaAutoConfigurationGenerator.kt
deleted file mode 100644
index 997a70e2..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaAutoConfigurationGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationModel
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class JavaSpringKafkaAutoConfigurationGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("java")
-
- fun generate(model: AutoConfigurationModel) {
- val template = mustacheFactory.compile("spring-kafka-autoconfiguration.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.className}.java")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleConsumerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaConsumerGenerator.kt
similarity index 85%
rename from asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleConsumerGenerator.kt
rename to asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaConsumerGenerator.kt
index fd49d66b..99daab45 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleConsumerGenerator.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaConsumerGenerator.kt
@@ -6,13 +6,13 @@ import dev.banking.asyncapi.generator.core.generator.util.FileUtil
import java.io.File
import java.io.StringWriter
-class JavaSpringKafkaSimpleConsumerGenerator(
+class JavaSpringKafkaConsumerGenerator(
private val outputDir: File,
) {
private val mustacheFactory = DefaultMustacheFactory("java")
fun generate(model: GeneratorItem.KafkaHandlerInterface) {
- val template = mustacheFactory.compile("spring-kafka-simple-consumer.mustache")
+ val template = mustacheFactory.compile("spring-kafka-consumer.mustache")
val writer = StringWriter()
template.execute(writer, model).flush()
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaGenerator.kt
index 7380fad2..ab466e76 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaGenerator.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaGenerator.kt
@@ -1,53 +1,37 @@
package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.java.factory.JavaKafkaGeneratorModelFactory
+import dev.banking.asyncapi.generator.core.generator.java.factory.JavaSpringKafkaModelFactory
import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationModel
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationResourceGenerator
import java.io.File
class JavaSpringKafkaGenerator(
outputDir: File,
- private val clientPackage: String,
+ clientPackage: String,
modelPackage: String,
- topicPropertyPrefix: String,
- resourceOutputDir: File,
+ generateHeaders: Boolean = true,
+ generateProducers: Boolean = true,
+ generateConsumers: Boolean = true,
) {
private val modelFactory =
- JavaKafkaGeneratorModelFactory(
- this.clientPackage,
- modelPackage,
- topicPropertyPrefix,
+ JavaSpringKafkaModelFactory(
+ clientPackage = clientPackage,
+ modelPackage = modelPackage,
+ generateHeaders = generateHeaders,
+ generateProducers = generateProducers,
+ generateConsumers = generateConsumers,
)
-
- private val handlerGenerator = JavaSpringKafkaHandlerGenerator(outputDir)
- private val listenerGenerator = JavaSpringKafkaListenerGenerator(outputDir)
private val producerGenerator = JavaSpringKafkaProducerGenerator(outputDir)
- private val autoConfigGenerator = JavaSpringKafkaAutoConfigurationGenerator(outputDir)
- private val autoConfigResourceGenerator = AutoConfigurationResourceGenerator(resourceOutputDir)
+ private val consumerGenerator = JavaSpringKafkaConsumerGenerator(outputDir)
fun generate(channels: List) {
- val autoConfigPackage = "${this.clientPackage}.config"
- val autoConfigClass = "AsyncApiKafkaAutoConfiguration"
- autoConfigGenerator.generate(
- AutoConfigurationModel(
- packageName = autoConfigPackage,
- className = autoConfigClass,
- clientPackage = this.clientPackage,
- ),
- )
- autoConfigResourceGenerator.generate("$autoConfigPackage.$autoConfigClass")
-
channels.forEach { channel ->
val items = modelFactory.create(channel)
items.forEach { item ->
when (item) {
- is GeneratorItem.KafkaHandlerInterface -> handlerGenerator.generate(item)
- is GeneratorItem.KafkaListenerClass -> listenerGenerator.generate(item)
is GeneratorItem.KafkaProducerClass -> producerGenerator.generate(item)
- else -> { // Ignore
- }
+ is GeneratorItem.KafkaHandlerInterface -> consumerGenerator.generate(item)
+ else -> { /* Ignore */ }
}
}
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaHandlerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaHandlerGenerator.kt
deleted file mode 100644
index 8595de36..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaHandlerGenerator.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class JavaSpringKafkaHandlerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("java")
-
- fun generate(model: GeneratorItem.KafkaHandlerInterface) {
- val template = mustacheFactory.compile("spring-kafka-handler.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val file = File(packageDir, "${model.name}.java")
- file.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaListenerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaListenerGenerator.kt
deleted file mode 100644
index 4dbb7b52..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaListenerGenerator.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class JavaSpringKafkaListenerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("java")
-
- fun generate(model: GeneratorItem.KafkaListenerClass) {
- val template = mustacheFactory.compile("spring-kafka-listener.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val file = File(packageDir, "${model.name}.java")
- file.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaProducerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaProducerGenerator.kt
index 7768802d..e67cf9a5 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaProducerGenerator.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaProducerGenerator.kt
@@ -17,7 +17,8 @@ class JavaSpringKafkaProducerGenerator(
template.execute(writer, model).flush()
val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val file = File(packageDir, "${model.name}.java")
- file.writeText(writer.toString())
+ val outputFile = File(packageDir, "${model.name}.java")
+ outputFile.parentFile.mkdirs()
+ outputFile.writeText(writer.toString())
}
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleGenerator.kt
deleted file mode 100644
index dfd041c6..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleGenerator.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
-
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.java.factory.JavaSpringKafkaSimpleModelFactory
-import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import java.io.File
-
-class JavaSpringKafkaSimpleGenerator(
- outputDir: File,
- clientPackage: String,
- modelPackage: String,
-) {
- private val modelFactory = JavaSpringKafkaSimpleModelFactory(clientPackage, modelPackage)
- private val producerGenerator = JavaSpringKafkaSimpleProducerGenerator(outputDir)
- private val consumerGenerator = JavaSpringKafkaSimpleConsumerGenerator(outputDir)
-
- fun generate(channels: List) {
- channels.forEach { channel ->
- val items = modelFactory.create(channel)
- items.forEach { item ->
- when (item) {
- is GeneratorItem.KafkaProducerClass -> producerGenerator.generate(item)
- is GeneratorItem.KafkaHandlerInterface -> consumerGenerator.generate(item)
- else -> { /* Ignore */ }
- }
- }
- }
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleProducerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleProducerGenerator.kt
deleted file mode 100644
index 59e88594..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/spring/JavaSpringKafkaSimpleProducerGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.java.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class JavaSpringKafkaSimpleProducerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("java")
-
- fun generate(model: GeneratorItem.KafkaProducerClass) {
- val template = mustacheFactory.compile("spring-kafka-simple-producer.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.name}.java")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/model/GeneratorItem.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/model/GeneratorItem.kt
index f0004887..3aededb2 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/model/GeneratorItem.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/java/model/GeneratorItem.kt
@@ -41,19 +41,6 @@ sealed interface GeneratorItem {
val imports: List = emptyList(),
) : GeneratorItem
- data class KafkaListenerClass(
- override val name: String,
- override val packageName: String,
- override val description: List,
- val topic: String,
- val groupId: String,
- val handlerInterface: String,
- val payloadType: String,
- val methodName: String,
- val imports: List = emptyList(),
- val topicPropertyKey: String,
- ) : GeneratorItem
-
data class KafkaProducerClass(
override val name: String,
override val packageName: String,
@@ -62,16 +49,27 @@ sealed interface GeneratorItem {
val sendMethods: List,
val kafkaValueType: String,
val imports: List = emptyList(),
- val topicPropertyKey: String,
) : GeneratorItem
data class HandlerMethod(
val methodName: String,
val payloadType: String,
- )
+ val headerType: String? = null,
+ ) {
+ val hasHeaders: Boolean get() = headerType != null
+ }
data class SendMethod(
val methodName: String,
val payloadType: String,
+ val headerType: String? = null,
+ val headerProperties: List = emptyList(),
+ ) {
+ val hasHeaders: Boolean get() = headerType != null
+ }
+
+ data class HeaderProperty(
+ val name: String,
+ val accessorName: String,
)
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationModel.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationModel.kt
deleted file mode 100644
index c23ba864..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationModel.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kafka.spring
-
-data class AutoConfigurationModel(
- val packageName: String,
- val className: String,
- val clientPackage: String,
-)
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationResourceGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationResourceGenerator.kt
deleted file mode 100644
index 08972e7d..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/AutoConfigurationResourceGenerator.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kafka.spring
-
-import java.io.File
-
-class AutoConfigurationResourceGenerator(
- private val resourceOutputDir: File,
-) {
- fun generate(autoConfigClassName: String) {
- val file = File(resourceOutputDir, "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")
- file.parentFile.mkdirs()
- val existing = if (file.exists()) file.readText().lines().filter { it.isNotBlank() } else emptyList()
- val updated = (existing + autoConfigClassName).distinct().joinToString("\n")
- file.writeText(updated + "\n")
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/KafkaPayload.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/KafkaPayload.kt
index a487d88f..0b86aac6 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/KafkaPayload.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/KafkaPayload.kt
@@ -7,4 +7,12 @@ data class KafkaPayload(
val messageName: String,
val payloadType: String,
val importName: String? = null,
+ val headerTypeName: String? = null,
+ val headerImportName: String? = null,
+ val headerProperties: List = emptyList(),
+)
+
+data class KafkaHeaderProperty(
+ val name: String,
+ val accessorName: String,
)
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/NativeKafkaPayloadResolver.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/NativeKafkaPayloadResolver.kt
index e78bf27a..b6a823cc 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/NativeKafkaPayloadResolver.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/NativeKafkaPayloadResolver.kt
@@ -9,9 +9,7 @@ import dev.banking.asyncapi.generator.core.generator.protobuf.NativeProtobufPayl
*
* Expected behavior is covered by:
* - `GenerateKotlinSpringKafkaTest`
- * - `GenerateKotlinSpringKafkaSimpleTest`
- * - `GenerateJavaSpringKafkaClientTest`
- * - `GenerateJavaSpringKafkaSimpleTest`
+ * - `GenerateJavaSpringKafkaTest`
*/
class NativeKafkaPayloadResolver(
private val nativeAvroPayloadTypeResolver: NativeAvroPayloadTypeResolver = NativeAvroPayloadTypeResolver(),
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGeneration.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGeneration.kt
index f4e36603..c10066c8 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGeneration.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGeneration.kt
@@ -2,20 +2,14 @@ package dev.banking.asyncapi.generator.core.generator.kafka.spring
import dev.banking.asyncapi.generator.core.generator.input.GenerationInput
import dev.banking.asyncapi.generator.core.generator.java.kafka.spring.JavaSpringKafkaGenerator
-import dev.banking.asyncapi.generator.core.generator.java.kafka.spring.JavaSpringKafkaSimpleGenerator
import dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring.KotlinSpringKafkaGenerator
-import dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring.KotlinSpringKafkaSimpleGenerator
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName.JAVA
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName.KOTLIN
import dev.banking.asyncapi.generator.core.generator.plan.GenerationTask
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import java.io.File
/**
- * Dispatches planned Spring Kafka client generation to the current implementations.
- *
- * This is the boundary for the existing Spring Kafka client generators while
- * the long-term client surface is still being designed.
+ * Dispatches planned Spring Kafka client generation to the supported contract generator.
*
* Expected behavior is covered by:
* - `SpringKafkaClientGenerationTest`
@@ -25,11 +19,12 @@ class SpringKafkaClientGeneration {
task: GenerationTask.SpringKafkaClient,
generationInput: GenerationInput,
sourceOutputDirectory: File,
+ @Suppress("UNUSED_PARAMETER")
resourceOutputDirectory: File,
) {
when (task.language) {
- KOTLIN -> generateKotlinClient(task, generationInput, sourceOutputDirectory, resourceOutputDirectory)
- JAVA -> generateJavaClient(task, generationInput, sourceOutputDirectory, resourceOutputDirectory)
+ KOTLIN -> generateKotlinClient(task, generationInput, sourceOutputDirectory)
+ JAVA -> generateJavaClient(task, generationInput, sourceOutputDirectory)
}
}
@@ -37,53 +32,33 @@ class SpringKafkaClientGeneration {
task: GenerationTask.SpringKafkaClient,
generationInput: GenerationInput,
sourceOutputDirectory: File,
- resourceOutputDirectory: File,
) {
- if (task.clientType == SpringKafkaClientType.SIMPLE) {
- val kafkaGenerator =
- KotlinSpringKafkaSimpleGenerator(
- outputDir = sourceOutputDirectory,
- clientPackage = task.clientPackage,
- modelPackage = task.modelPackage,
- )
- kafkaGenerator.generate(generationInput.channels)
- } else {
- val kafkaGenerator =
- KotlinSpringKafkaGenerator(
- outputDir = sourceOutputDirectory,
- clientPackage = task.clientPackage,
- modelPackage = task.modelPackage,
- topicPropertyPrefix = task.topicPropertyPrefix,
- resourceOutputDir = resourceOutputDirectory,
- )
- kafkaGenerator.generate(generationInput.channels)
- }
+ val kafkaGenerator =
+ KotlinSpringKafkaGenerator(
+ outputDir = sourceOutputDirectory,
+ clientPackage = task.clientPackage,
+ modelPackage = task.modelPackage,
+ generateHeaders = task.generateHeaders,
+ generateProducers = task.generateProducers,
+ generateConsumers = task.generateConsumers,
+ )
+ kafkaGenerator.generate(generationInput.channels)
}
private fun generateJavaClient(
task: GenerationTask.SpringKafkaClient,
generationInput: GenerationInput,
sourceOutputDirectory: File,
- resourceOutputDirectory: File,
) {
- if (task.clientType == SpringKafkaClientType.SIMPLE) {
- val kafkaGenerator =
- JavaSpringKafkaSimpleGenerator(
- outputDir = sourceOutputDirectory,
- clientPackage = task.clientPackage,
- modelPackage = task.modelPackage,
- )
- kafkaGenerator.generate(generationInput.channels)
- } else {
- val kafkaGenerator =
- JavaSpringKafkaGenerator(
- outputDir = sourceOutputDirectory,
- clientPackage = task.clientPackage,
- modelPackage = task.modelPackage,
- topicPropertyPrefix = task.topicPropertyPrefix,
- resourceOutputDir = resourceOutputDirectory,
- )
- kafkaGenerator.generate(generationInput.channels)
- }
+ val kafkaGenerator =
+ JavaSpringKafkaGenerator(
+ outputDir = sourceOutputDirectory,
+ clientPackage = task.clientPackage,
+ modelPackage = task.modelPackage,
+ generateHeaders = task.generateHeaders,
+ generateProducers = task.generateProducers,
+ generateConsumers = task.generateConsumers,
+ )
+ kafkaGenerator.generate(generationInput.channels)
}
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinKafkaGeneratorModelFactory.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinKafkaGeneratorModelFactory.kt
deleted file mode 100644
index 443f3b4c..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinKafkaGeneratorModelFactory.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.factory
-
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessage
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaPayload
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.NativeKafkaPayloadResolver
-import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.DocumentationUtils.toKDocLines
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryType
-
-class KotlinKafkaGeneratorModelFactory(
- private val packageName: String,
- private val modelPackage: String,
- private val topicPropertyPrefix: String,
- private val nativeKafkaPayloadResolver: NativeKafkaPayloadResolver = NativeKafkaPayloadResolver(),
-) {
- fun create(channel: AnalyzedChannel): List {
- val items = mutableListOf()
- val baseName = MapperUtil.toPascalCase(channel.channelName)
- val handlerPackage = "$packageName.handler"
- val listenerPackage = "$packageName.listener"
- val producerPackage = "$packageName.producer"
- val payloads = channel.payloads()
-
- val baseImports =
- payloads.mapNotNull { payload -> payload.importName }
- val imports =
- (baseImports + "org.apache.kafka.clients.consumer.ConsumerRecord")
- .distinct()
- .sorted()
-
- if (channel.isConsumer) {
- val topicPropertyKey = topicPropertyKey(channel.channelName)
- val topicPrefix = "Topic$baseName"
- payloads.forEach { payload ->
- val methodName = "on${payload.messageName}"
- val handlerName = "${topicPrefix}Handler${payload.messageName}"
- items.add(
- GeneratorItem.KafkaHandlerInterface(
- name = handlerName,
- packageName = handlerPackage,
- description = toKDocLines("Handler for messages on topic '${channel.topic}'"),
- methods =
- listOf(
- GeneratorItem.HandlerMethod(
- methodName = methodName,
- payloadType = payload.payloadType,
- keyType = "String?",
- ),
- ),
- imports = imports,
- ),
- )
- val listenerName = "${topicPrefix}Listener${payload.messageName}"
- val listenerImports = (imports + "$handlerPackage.$handlerName").distinct().sorted()
- items.add(
- GeneratorItem.KafkaListenerClass(
- name = listenerName,
- packageName = listenerPackage,
- description = toKDocLines("Spring Kafka Listener for topic '${channel.topic}'"),
- topic = channel.topic,
- groupId = "\\\${spring.kafka.consumer.group-id}",
- handlerInterface = handlerName,
- payloadType = payload.payloadType,
- methodName = methodName,
- imports = listenerImports,
- topicPropertyKey = topicPropertyKey,
- ),
- )
- }
- }
-
- if (channel.isProducer) {
- val topicPropertyKey = topicPropertyKey(channel.channelName)
- val topicPrefix = "Topic$baseName"
- payloads.forEach { payload ->
- val sendMethod =
- GeneratorItem.SendMethod(
- methodName = "send${payload.messageName}",
- payloadType = payload.payloadType,
- keyType = "String",
- )
- val producerName = "${topicPrefix}Producer${payload.messageName}"
- items.add(
- GeneratorItem.KafkaProducerClass(
- name = producerName,
- packageName = producerPackage,
- description = toKDocLines("Producer for topic '${channel.topic}'"),
- topic = channel.topic,
- sendMethods = listOf(sendMethod),
- kafkaValueType = payload.payloadType,
- imports = imports,
- topicPropertyKey = topicPropertyKey,
- ),
- )
- }
- }
- return items
- }
-
- private fun topicPropertyKey(channelName: String): String = "$topicPropertyPrefix.$channelName"
-
- private fun AnalyzedChannel.payloads(): List =
- messages.map(::payload) + multiFormatMessages.mapNotNull(nativeKafkaPayloadResolver::resolve)
-
- private fun payload(msg: AnalyzedMessage): KafkaPayload {
- val type = resolvePayloadType(msg)
- return KafkaPayload(
- messageName = msg.messageName,
- payloadType = type,
- importName =
- if (isPrimitive(type)) {
- null
- } else {
- "$modelPackage.$type"
- },
- )
- }
-
- private fun resolvePayloadType(msg: AnalyzedMessage): String =
- when (msg.schema.type.getPrimaryType()) {
- "string" -> "String"
- "integer" -> "Int" // Simplified, could check format for Long
- "number" -> "java.math.BigDecimal"
- "boolean" -> "Boolean"
- else -> msg.payloadTypeName // Object types use the Class Name
- }
-
- private fun isPrimitive(type: String): Boolean = type in setOf("String", "Int", "Long", "Boolean", "java.math.BigDecimal")
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaSimpleModelFactory.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaModelFactory.kt
similarity index 57%
rename from asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaSimpleModelFactory.kt
rename to asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaModelFactory.kt
index 24388698..d0c8661a 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaSimpleModelFactory.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/factory/KotlinSpringKafkaModelFactory.kt
@@ -2,6 +2,8 @@ package dev.banking.asyncapi.generator.core.generator.kotlin.factory
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessage
+import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedMessageHeaders
+import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaHeaderProperty
import dev.banking.asyncapi.generator.core.generator.kafka.spring.KafkaPayload
import dev.banking.asyncapi.generator.core.generator.kafka.spring.NativeKafkaPayloadResolver
import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
@@ -9,9 +11,12 @@ import dev.banking.asyncapi.generator.core.generator.util.DocumentationUtils.toK
import dev.banking.asyncapi.generator.core.generator.util.MapperUtil
import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryType
-class KotlinSpringKafkaSimpleModelFactory(
+class KotlinSpringKafkaModelFactory(
private val clientPackage: String,
private val modelPackage: String,
+ private val generateHeaders: Boolean = true,
+ private val generateProducers: Boolean = true,
+ private val generateConsumers: Boolean = true,
private val nativeKafkaPayloadResolver: NativeKafkaPayloadResolver = NativeKafkaPayloadResolver(),
) {
fun create(channel: AnalyzedChannel): List {
@@ -22,9 +27,11 @@ class KotlinSpringKafkaSimpleModelFactory(
val payloads = channel.payloads()
val baseImports =
- payloads.mapNotNull { payload -> payload.importName }
+ payloads.flatMap { payload -> listOfNotNull(payload.importName, payload.headerImportName) }
+ .distinct()
+ .sorted()
- if (channel.isConsumer) {
+ if (channel.isConsumer && generateConsumers) {
val consumerName = "${baseName}Consumer"
val imports = (baseImports + "org.apache.kafka.clients.consumer.ConsumerRecord").distinct().sorted()
val methods =
@@ -33,6 +40,7 @@ class KotlinSpringKafkaSimpleModelFactory(
methodName = "on${payload.messageName}",
payloadType = payload.payloadType,
keyType = "String?",
+ headerType = payload.headerTypeName,
)
}
items.add(
@@ -46,9 +54,15 @@ class KotlinSpringKafkaSimpleModelFactory(
)
}
- if (channel.isProducer) {
+ if (channel.isProducer && generateProducers) {
val imports =
- (baseImports + "org.apache.kafka.clients.producer.ProducerRecord" + "org.springframework.kafka.core.KafkaTemplate")
+ (
+ baseImports +
+ "java.util.concurrent.CompletableFuture" +
+ "org.apache.kafka.clients.producer.ProducerRecord" +
+ "org.springframework.kafka.core.KafkaTemplate" +
+ "org.springframework.kafka.support.SendResult"
+ )
.distinct()
.sorted()
payloads.forEach { payload ->
@@ -57,6 +71,14 @@ class KotlinSpringKafkaSimpleModelFactory(
methodName = "send${payload.messageName}",
payloadType = payload.payloadType,
keyType = "String",
+ headerType = payload.headerTypeName,
+ headerProperties =
+ payload.headerProperties.map { header ->
+ GeneratorItem.HeaderProperty(
+ name = header.name,
+ accessorName = header.accessorName,
+ )
+ },
)
val producerName = "${baseName}Producer${payload.messageName}"
items.add(
@@ -68,7 +90,6 @@ class KotlinSpringKafkaSimpleModelFactory(
sendMethods = listOf(sendMethod),
kafkaValueType = payload.payloadType,
imports = imports,
- topicPropertyKey = "",
),
)
}
@@ -78,10 +99,15 @@ class KotlinSpringKafkaSimpleModelFactory(
}
private fun AnalyzedChannel.payloads(): List =
- messages.map(::payload) + multiFormatMessages.mapNotNull(nativeKafkaPayloadResolver::resolve)
+ messages.map(::payload) +
+ multiFormatMessages.mapNotNull { message ->
+ nativeKafkaPayloadResolver.resolve(message)
+ ?.withHeaders(message.headers)
+ }
private fun payload(msg: AnalyzedMessage): KafkaPayload {
val type = resolvePayloadType(msg)
+ val headers = if (generateHeaders) msg.headers else null
return KafkaPayload(
messageName = msg.messageName,
payloadType = type,
@@ -91,6 +117,19 @@ class KotlinSpringKafkaSimpleModelFactory(
} else {
"$modelPackage.$type"
},
+ headerTypeName = headers?.typeName,
+ headerImportName = headers?.typeName?.let { "$clientPackage.header.$it" },
+ headerProperties =
+ headers
+ ?.properties
+ ?.keys
+ ?.map { headerName ->
+ KafkaHeaderProperty(
+ name = headerName,
+ accessorName = headerName,
+ )
+ }
+ .orEmpty(),
)
}
@@ -104,4 +143,25 @@ class KotlinSpringKafkaSimpleModelFactory(
}
private fun isPrimitive(type: String): Boolean = type in setOf("String", "Int", "Long", "Boolean", "java.math.BigDecimal")
+
+ private fun KafkaPayload.withHeaders(headers: AnalyzedMessageHeaders?): KafkaPayload =
+ if (generateHeaders) {
+ copy(
+ headerTypeName = headers?.typeName,
+ headerImportName = headers?.typeName?.let { "$clientPackage.header.$it" },
+ headerProperties =
+ headers
+ ?.properties
+ ?.keys
+ ?.map { headerName ->
+ KafkaHeaderProperty(
+ name = headerName,
+ accessorName = headerName,
+ )
+ }
+ .orEmpty(),
+ )
+ } else {
+ this
+ }
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaAutoConfigurationGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaAutoConfigurationGenerator.kt
deleted file mode 100644
index cd7b3233..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaAutoConfigurationGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationModel
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class KotlinSpringKafkaAutoConfigurationGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("kotlin")
-
- fun generate(model: AutoConfigurationModel) {
- val template = mustacheFactory.compile("spring-kafka-autoconfiguration.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.className}.kt")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaHandlerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaConsumerGenerator.kt
similarity index 86%
rename from asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaHandlerGenerator.kt
rename to asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaConsumerGenerator.kt
index 10fc0398..3df32f07 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaHandlerGenerator.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaConsumerGenerator.kt
@@ -6,13 +6,13 @@ import dev.banking.asyncapi.generator.core.generator.util.FileUtil
import java.io.File
import java.io.StringWriter
-class KotlinSpringKafkaHandlerGenerator(
+class KotlinSpringKafkaConsumerGenerator(
private val outputDir: File,
) {
private val mustacheFactory = DefaultMustacheFactory("kotlin")
fun generate(model: GeneratorItem.KafkaHandlerInterface) {
- val template = mustacheFactory.compile("spring-kafka-handler.mustache")
+ val template = mustacheFactory.compile("spring-kafka-consumer.mustache")
val writer = StringWriter()
template.execute(writer, model).flush()
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaGenerator.kt
index 78c08269..5f8ff711 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaGenerator.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaGenerator.kt
@@ -1,51 +1,37 @@
package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationModel
-import dev.banking.asyncapi.generator.core.generator.kafka.spring.AutoConfigurationResourceGenerator
-import dev.banking.asyncapi.generator.core.generator.kotlin.factory.KotlinKafkaGeneratorModelFactory
+import dev.banking.asyncapi.generator.core.generator.kotlin.factory.KotlinSpringKafkaModelFactory
import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
import java.io.File
class KotlinSpringKafkaGenerator(
outputDir: File,
- private val clientPackage: String,
+ clientPackage: String,
modelPackage: String,
- topicPropertyPrefix: String,
- resourceOutputDir: File,
+ generateHeaders: Boolean = true,
+ generateProducers: Boolean = true,
+ generateConsumers: Boolean = true,
) {
private val modelFactory =
- KotlinKafkaGeneratorModelFactory(
- this.clientPackage,
- modelPackage,
- topicPropertyPrefix,
+ KotlinSpringKafkaModelFactory(
+ clientPackage = clientPackage,
+ modelPackage = modelPackage,
+ generateHeaders = generateHeaders,
+ generateProducers = generateProducers,
+ generateConsumers = generateConsumers,
)
- private val handlerGenerator = KotlinSpringKafkaHandlerGenerator(outputDir)
- private val listenerGenerator = KotlinSpringKafkaListenerGenerator(outputDir)
private val producerGenerator = KotlinSpringKafkaProducerGenerator(outputDir)
- private val autoConfigGenerator = KotlinSpringKafkaAutoConfigurationGenerator(outputDir)
- private val autoConfigResourceGenerator = AutoConfigurationResourceGenerator(resourceOutputDir)
+ private val consumerGenerator = KotlinSpringKafkaConsumerGenerator(outputDir)
fun generate(channels: List) {
- val autoConfigPackage = "${this.clientPackage}.config"
- val autoConfigClass = "AsyncApiKafkaAutoConfiguration"
- autoConfigGenerator.generate(
- AutoConfigurationModel(
- packageName = autoConfigPackage,
- className = autoConfigClass,
- clientPackage = this.clientPackage,
- ),
- )
- autoConfigResourceGenerator.generate("$autoConfigPackage.$autoConfigClass")
-
channels.forEach { channel ->
val items = modelFactory.create(channel)
items.forEach { item ->
when (item) {
- is GeneratorItem.KafkaHandlerInterface -> handlerGenerator.generate(item)
- is GeneratorItem.KafkaListenerClass -> listenerGenerator.generate(item)
is GeneratorItem.KafkaProducerClass -> producerGenerator.generate(item)
- else -> { /* Ignore other types if mixed */ }
+ is GeneratorItem.KafkaHandlerInterface -> consumerGenerator.generate(item)
+ else -> { /* Ignore */ }
}
}
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaListenerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaListenerGenerator.kt
deleted file mode 100644
index 6b6971d5..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaListenerGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class KotlinSpringKafkaListenerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("kotlin")
-
- fun generate(model: GeneratorItem.KafkaListenerClass) {
- val template = mustacheFactory.compile("spring-kafka-listener.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.name}.kt")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleConsumerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleConsumerGenerator.kt
deleted file mode 100644
index 9cf69ce5..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleConsumerGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class KotlinSpringKafkaSimpleConsumerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("kotlin")
-
- fun generate(model: GeneratorItem.KafkaHandlerInterface) {
- val template = mustacheFactory.compile("spring-kafka-simple-consumer.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.name}.kt")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleGenerator.kt
deleted file mode 100644
index 4c188681..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleGenerator.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
-
-import dev.banking.asyncapi.generator.core.generator.analyzer.AnalyzedChannel
-import dev.banking.asyncapi.generator.core.generator.kotlin.factory.KotlinSpringKafkaSimpleModelFactory
-import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
-import java.io.File
-
-class KotlinSpringKafkaSimpleGenerator(
- outputDir: File,
- clientPackage: String,
- modelPackage: String,
-) {
- private val modelFactory = KotlinSpringKafkaSimpleModelFactory(clientPackage, modelPackage)
- private val producerGenerator = KotlinSpringKafkaSimpleProducerGenerator(outputDir)
- private val consumerGenerator = KotlinSpringKafkaSimpleConsumerGenerator(outputDir)
-
- fun generate(channels: List) {
- channels.forEach { channel ->
- val items = modelFactory.create(channel)
- items.forEach { item ->
- when (item) {
- is GeneratorItem.KafkaProducerClass -> producerGenerator.generate(item)
- is GeneratorItem.KafkaHandlerInterface -> consumerGenerator.generate(item)
- else -> { /* Ignore */ }
- }
- }
- }
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleProducerGenerator.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleProducerGenerator.kt
deleted file mode 100644
index 43af4a2d..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/spring/KotlinSpringKafkaSimpleProducerGenerator.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka.spring
-
-import com.github.mustachejava.DefaultMustacheFactory
-import dev.banking.asyncapi.generator.core.generator.kotlin.model.GeneratorItem
-import dev.banking.asyncapi.generator.core.generator.util.FileUtil
-import java.io.File
-import java.io.StringWriter
-
-class KotlinSpringKafkaSimpleProducerGenerator(
- private val outputDir: File,
-) {
- private val mustacheFactory = DefaultMustacheFactory("kotlin")
-
- fun generate(model: GeneratorItem.KafkaProducerClass) {
- val template = mustacheFactory.compile("spring-kafka-simple-producer.mustache")
- val writer = StringWriter()
- template.execute(writer, model).flush()
-
- val packageDir = FileUtil.packageDirectory(outputDir, model.packageName)
- val outputFile = File(packageDir, "${model.name}.kt")
- outputFile.parentFile.mkdirs()
- outputFile.writeText(writer.toString())
- }
-}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/model/GeneratorItem.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/model/GeneratorItem.kt
index 0cf98b75..56040e78 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/model/GeneratorItem.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/model/GeneratorItem.kt
@@ -44,19 +44,6 @@ sealed interface GeneratorItem {
val imports: List = emptyList(),
) : GeneratorItem
- data class KafkaListenerClass(
- override val name: String,
- override val packageName: String,
- override val description: List,
- val topic: String,
- val groupId: String,
- val handlerInterface: String, // The interface to inject
- val payloadType: String,
- val methodName: String,
- val imports: List = emptyList(),
- val topicPropertyKey: String,
- ) : GeneratorItem
-
data class KafkaProducerClass(
override val name: String,
override val packageName: String,
@@ -65,18 +52,29 @@ sealed interface GeneratorItem {
val sendMethods: List,
val kafkaValueType: String,
val imports: List = emptyList(),
- val topicPropertyKey: String,
) : GeneratorItem
data class HandlerMethod(
val methodName: String,
val payloadType: String,
val keyType: String?,
- )
+ val headerType: String? = null,
+ ) {
+ val hasHeaders: Boolean get() = headerType != null
+ }
data class SendMethod(
val methodName: String,
val payloadType: String,
val keyType: String?,
+ val headerType: String? = null,
+ val headerProperties: List = emptyList(),
+ ) {
+ val hasHeaders: Boolean get() = headerType != null
+ }
+
+ data class HeaderProperty(
+ val name: String,
+ val accessorName: String,
)
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/loader/HeaderSchemaCollector.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/loader/HeaderSchemaCollector.kt
index 72724a85..7b07f1fc 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/loader/HeaderSchemaCollector.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/loader/HeaderSchemaCollector.kt
@@ -1,16 +1,12 @@
package dev.banking.asyncapi.generator.core.generator.loader
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil
-import dev.banking.asyncapi.generator.core.generator.util.MapperUtil.getPrimaryType
+import dev.banking.asyncapi.generator.core.generator.analyzer.MessageHeaderAnalyzer
import dev.banking.asyncapi.generator.core.model.asyncapi.AsyncApiDocument
import dev.banking.asyncapi.generator.core.model.channels.Channel
import dev.banking.asyncapi.generator.core.model.channels.ChannelInterface
import dev.banking.asyncapi.generator.core.model.messages.Message
import dev.banking.asyncapi.generator.core.model.messages.MessageInterface
-import dev.banking.asyncapi.generator.core.model.messages.MessageTrait
-import dev.banking.asyncapi.generator.core.model.messages.MessageTraitInterface
import dev.banking.asyncapi.generator.core.model.schemas.Schema
-import dev.banking.asyncapi.generator.core.model.schemas.SchemaInterface
object HeaderSchemaCollector {
fun collect(asyncApiDocument: AsyncApiDocument): Map {
@@ -28,49 +24,19 @@ object HeaderSchemaCollector {
is MessageInterface.MessageInline -> messageInterface.message
is MessageInterface.MessageReference -> messageInterface.reference.model as? Message
} ?: return@forEach
- val headers = collectHeaders(message)
- if (headers.isEmpty()) return@forEach
- val channelNamePascal = MapperUtil.toPascalCase(channelName)
- val messageName = message.name ?: message.title ?: messageKey
- val messageNamePascal = MapperUtil.toPascalCase(messageName)
- val schemaName = "Topic${channelNamePascal}Headers$messageNamePascal"
- headerSchemas[schemaName] =
+ val headers =
+ MessageHeaderAnalyzer.analyze(
+ channelName = channelName,
+ messageKey = messageKey,
+ message = message,
+ ) ?: return@forEach
+ headerSchemas[headers.typeName] =
Schema(
type = "object",
- properties = headers,
+ properties = headers.properties,
)
}
}
return headerSchemas
}
-
- private fun collectHeaders(message: Message): Map {
- val headers = mutableMapOf()
-
- message.traits?.forEach { traitInterface ->
- val trait =
- when (traitInterface) {
- is MessageTraitInterface.InlineMessageTrait -> traitInterface.trait
- is MessageTraitInterface.ReferenceMessageTrait -> traitInterface.reference.model as? MessageTrait
- }
- trait?.headers?.let { headers.putAll(extractHeaderProperties(it)) }
- }
-
- message.headers?.let { headers.putAll(extractHeaderProperties(it)) }
-
- return headers
- }
-
- private fun extractHeaderProperties(schemaInterface: SchemaInterface): Map =
- when (schemaInterface) {
- is SchemaInterface.SchemaInline -> {
- val schema = schemaInterface.schema
- if (schema.type.getPrimaryType() == "object") schema.properties ?: emptyMap() else emptyMap()
- }
- is SchemaInterface.SchemaReference -> {
- val schema = schemaInterface.reference.model as? Schema
- if (schema?.type.getPrimaryType() == "object") schema?.properties ?: emptyMap() else emptyMap()
- }
- else -> emptyMap()
- }
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlanner.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlanner.kt
index 9b364e25..bcaeff5a 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlanner.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlanner.kt
@@ -30,12 +30,8 @@ class GenerationPlanner {
configuration.clients.forEach { client ->
when (client) {
- is ClientGeneration.SpringKafka -> {
- require(client.topicPropertyPrefix.isNotBlank()) {
- "topicPropertyPrefix cannot be empty"
- }
-
- if (client.clientType != SpringKafkaClientType.SIMPLE) {
+ is ClientGeneration.Kafka -> {
+ if (client.headers.enabled) {
add(
GenerationTask.HeaderModelArtifacts(
language = configuration.language,
@@ -43,15 +39,18 @@ class GenerationPlanner {
),
)
}
- add(
- GenerationTask.SpringKafkaClient(
- language = configuration.language,
- clientType = client.clientType,
- clientPackage = client.packageName,
- modelPackage = client.modelPackageName,
- topicPropertyPrefix = client.topicPropertyPrefix,
- ),
- )
+ client.springKafka?.takeIf { it.hasEnabledOutput() }?.let { springKafka ->
+ add(
+ GenerationTask.SpringKafkaClient(
+ language = configuration.language,
+ clientPackage = client.packageName,
+ modelPackage = client.modelPackageName,
+ generateHeaders = client.headers.enabled,
+ generateProducers = springKafka.producer.enabled,
+ generateConsumers = springKafka.consumer.enabled,
+ ),
+ )
+ }
}
is ClientGeneration.QuarkusKafka ->
add(GenerationTask.QuarkusKafkaClient(configuration.language))
@@ -79,4 +78,6 @@ class GenerationPlanner {
},
)
+ private fun ClientGeneration.SpringKafka.hasEnabledOutput(): Boolean =
+ producer.enabled || consumer.enabled
}
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationTask.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationTask.kt
index 5e529885..d0b4ec6d 100644
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationTask.kt
+++ b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationTask.kt
@@ -24,10 +24,11 @@ sealed interface GenerationTask {
data class SpringKafkaClient(
val language: GeneratorName,
- val clientType: SpringKafkaClientType,
val clientPackage: String,
val modelPackage: String,
- val topicPropertyPrefix: String,
+ val generateHeaders: Boolean = true,
+ val generateProducers: Boolean = true,
+ val generateConsumers: Boolean = true,
) : GenerationTask
data class QuarkusKafkaClient(
diff --git a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientType.kt b/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientType.kt
deleted file mode 100644
index c63d6363..00000000
--- a/asyncapi-generator-core/src/main/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientType.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.plan
-
-/**
- * Spring Kafka client generation mode selected during planning.
- *
- * The simple client remains the default when a caller enables Spring Kafka generation
- * without selecting another mode.
- *
- * Expected behavior is covered by:
- * - `GenerationPlannerTest`
- * - `SpringKafkaClientTypeTest`
- */
-enum class SpringKafkaClientType(
- val configurationValue: String,
-) {
- FULL("full"),
- SIMPLE("simple"),
- ;
-
- companion object {
- val supportedConfigurationValues: List = entries.map { it.configurationValue }
-
- fun fromConfigurationValue(
- value: String?,
- path: String,
- ): SpringKafkaClientType {
- if (value == null) {
- return SIMPLE
- }
-
- return entries.firstOrNull { it.configurationValue == value }
- ?: throw IllegalArgumentException(
- "Invalid $path '$value'. Supported values: ${supportedConfigurationValues.joinToString(", ")}",
- )
- }
- }
-}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-autoconfiguration.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-autoconfiguration.mustache
deleted file mode 100644
index 36fd97b0..00000000
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-autoconfiguration.mustache
+++ /dev/null
@@ -1,9 +0,0 @@
-package {{packageName}};
-
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-
-@AutoConfiguration
-@ComponentScan(basePackages = "{{clientPackage}}")
-public class {{className}} {
-}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-handler.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-consumer.mustache
similarity index 71%
rename from asyncapi-generator-core/src/main/resources/java/spring-kafka-handler.mustache
rename to asyncapi-generator-core/src/main/resources/java/spring-kafka-consumer.mustache
index dea03bc7..ab3db0bd 100644
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-handler.mustache
+++ b/asyncapi-generator-core/src/main/resources/java/spring-kafka-consumer.mustache
@@ -1,16 +1,16 @@
-package {{packageName}};
-
-{{#imports}}
-import {{{.}}};
-{{/imports}}
-
-/**
-{{#description}}
- * {{{.}}}
-{{/description}}
- */
-public interface {{name}} {
-{{#methods}}
- void {{methodName}}(ConsumerRecord record);
-{{/methods}}
-}
+package {{packageName}};
+
+{{#imports}}
+import {{.}};
+{{/imports}}
+
+/**
+{{#description}}
+ * {{.}}
+{{/description}}
+ */
+public interface {{name}} {
+{{#methods}}
+ void {{methodName}}(ConsumerRecord record{{#hasHeaders}}, {{headerType}} headers{{/hasHeaders}});
+{{/methods}}
+}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-listener.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-listener.mustache
deleted file mode 100644
index 153c45e1..00000000
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-listener.mustache
+++ /dev/null
@@ -1,36 +0,0 @@
-package {{packageName}};
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.stereotype.Component;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-{{#imports}}
-import {{.}};
-{{/imports}}
-
-/**
-{{#description}}
- * {{.}}
-{{/description}}
- */
-@Component
-@ConditionalOnBean({{handlerInterface}}.class)
-@ConditionalOnProperty(name = "{{topicPropertyKey}}")
-public class {{name}} {
-
- private static final Logger log = LoggerFactory.getLogger({{name}}.class);
- private final {{handlerInterface}} handler;
-
- public {{name}}({{handlerInterface}} handler) {
- this.handler = handler;
- }
-
- {{=<% %>=}}
- @KafkaListener(topics = "${<%topicPropertyKey%>}", groupId = "<%groupId%>")
- <%={{ }}=%> public void listen(ConsumerRecord record) {
- log.debug("Dispatching {{payloadType}} from topic {{topic}}");
- handler.{{methodName}}(record);
- }
-}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-producer.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-producer.mustache
index d2e4fffc..5c026d00 100644
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-producer.mustache
+++ b/asyncapi-generator-core/src/main/resources/java/spring-kafka-producer.mustache
@@ -1,12 +1,5 @@
package {{packageName}};
-import org.apache.kafka.clients.producer.ProducerRecord;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.stereotype.Component;
{{#imports}}
import {{{.}}};
{{/imports}}
@@ -16,25 +9,34 @@ import {{{.}}};
* {{{.}}}
{{/description}}
*/
-@Component
-@ConditionalOnProperty(name = "{{topicPropertyKey}}")
public class {{name}} {
- private static final Logger log = LoggerFactory.getLogger({{name}}.class);
private final KafkaTemplate kafkaTemplate;
+ private final String topic;
- {{=<% %>=}}
- @Value("${<%topicPropertyKey%>}")
- private String topic;
- <%={{ }}=%>
- public {{name}}(KafkaTemplate kafkaTemplate) {
+ public {{name}}(KafkaTemplate kafkaTemplate, String topic) {
this.kafkaTemplate = kafkaTemplate;
+ this.topic = topic;
}
{{#sendMethods}}
- public void {{methodName}}(String key, {{payloadType}} message) {
- log.info("Sending {{payloadType}} to topic={}, key={}", topic, key);
- kafkaTemplate.send(new ProducerRecord<>(topic, key, message));
+ public CompletableFuture> {{methodName}}(
+ String key,
+ {{payloadType}} message{{#hasHeaders}},
+ {{headerType}} headers{{/hasHeaders}}
+ ) {
+{{#hasHeaders}}
+ ProducerRecord record = new ProducerRecord<>(topic, key, message);
+{{#headerProperties}}
+ if (headers != null && headers.{{accessorName}}() != null) {
+ record.headers().add("{{name}}", String.valueOf(headers.{{accessorName}}()).getBytes(StandardCharsets.UTF_8));
+ }
+{{/headerProperties}}
+ return kafkaTemplate.send(record);
+{{/hasHeaders}}
+{{^hasHeaders}}
+ return kafkaTemplate.send(new ProducerRecord<>(topic, key, message));
+{{/hasHeaders}}
}
{{/sendMethods}}
}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-consumer.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-consumer.mustache
deleted file mode 100644
index 173621c0..00000000
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-consumer.mustache
+++ /dev/null
@@ -1,16 +0,0 @@
-package {{packageName}};
-
-{{#imports}}
-import {{.}};
-{{/imports}}
-
-/**
-{{#description}}
- * {{.}}
-{{/description}}
- */
-public interface {{name}} {
-{{#methods}}
- default void {{methodName}}(ConsumerRecord record) { }
-{{/methods}}
-}
diff --git a/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-producer.mustache b/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-producer.mustache
deleted file mode 100644
index acf76f6f..00000000
--- a/asyncapi-generator-core/src/main/resources/java/spring-kafka-simple-producer.mustache
+++ /dev/null
@@ -1,27 +0,0 @@
-package {{packageName}};
-
-{{#imports}}
-import {{{.}}};
-{{/imports}}
-
-/**
-{{#description}}
- * {{{.}}}
-{{/description}}
- */
-public class {{name}} {
-
- private final KafkaTemplate kafkaTemplate;
- private final String topic;
-
- public {{name}}(KafkaTemplate kafkaTemplate, String topic) {
- this.kafkaTemplate = kafkaTemplate;
- this.topic = topic;
- }
-
- {{#sendMethods}}
- public void {{methodName}}(String key, {{payloadType}} message) {
- kafkaTemplate.send(new ProducerRecord<>(topic, key, message));
- }
- {{/sendMethods}}
-}
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-autoconfiguration.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-autoconfiguration.mustache
deleted file mode 100644
index 05c56d28..00000000
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-autoconfiguration.mustache
+++ /dev/null
@@ -1,8 +0,0 @@
-package {{packageName}}
-
-import org.springframework.context.annotation.ComponentScan
-import org.springframework.boot.autoconfigure.AutoConfiguration
-
-@AutoConfiguration
-@ComponentScan(basePackages = ["{{clientPackage}}"])
-class {{className}}
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-consumer.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-consumer.mustache
similarity index 78%
rename from asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-consumer.mustache
rename to asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-consumer.mustache
index 9a874524..725f875f 100644
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-consumer.mustache
+++ b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-consumer.mustache
@@ -11,6 +11,6 @@ import {{{.}}}
*/
interface {{name}} {
{{#methods}}
- fun {{methodName}}(record: ConsumerRecord) { }
+ fun {{methodName}}(record: ConsumerRecord{{#hasHeaders}}, headers: {{headerType}}{{/hasHeaders}})
{{/methods}}
}
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-handler.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-handler.mustache
deleted file mode 100644
index 53671a16..00000000
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-handler.mustache
+++ /dev/null
@@ -1,16 +0,0 @@
-package {{packageName}}
-
-{{#imports}}
-import {{{.}}}
-{{/imports}}
-
-/**
-{{#description}}
- * {{{.}}}
-{{/description}}
- */
-interface {{name}} {
-{{#methods}}
- fun {{methodName}}(record: ConsumerRecord)
-{{/methods}}
-}
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-listener.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-listener.mustache
deleted file mode 100644
index 4985595a..00000000
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-listener.mustache
+++ /dev/null
@@ -1,32 +0,0 @@
-package {{packageName}}
-
-import org.slf4j.LoggerFactory
-import org.springframework.kafka.annotation.KafkaListener
-import org.springframework.stereotype.Component
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
-{{#imports}}
-import {{{.}}}
-{{/imports}}
-
-
-/**
-{{#description}}
- * {{{.}}}
-{{/description}}
- */
-@Component
-@ConditionalOnBean({{handlerInterface}}::class)
-@ConditionalOnProperty(name = ["{{topicPropertyKey}}"])
-class {{name}}(
- private val handler: {{handlerInterface}}
-) {
-
- private val log = LoggerFactory.getLogger({{name}}::class.java)
-{{=<% %>=}}
- @KafkaListener(topics = ["\${<%topicPropertyKey%>}"], groupId = "<%groupId%>")
-<%={{ }}=%> fun listen(record: ConsumerRecord) {
- log.debug("Dispatching {{payloadType}} from topic {{topic}}")
- handler.{{methodName}}(record)
- }
- }
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-producer.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-producer.mustache
index 73bc7eee..65ef424a 100644
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-producer.mustache
+++ b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-producer.mustache
@@ -1,11 +1,5 @@
package {{packageName}}
-import org.apache.kafka.clients.producer.ProducerRecord
-import org.slf4j.LoggerFactory
-import org.springframework.kafka.core.KafkaTemplate
-import org.springframework.beans.factory.annotation.Value
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
-import org.springframework.stereotype.Component
{{#imports}}
import {{{.}}}
{{/imports}}
@@ -15,20 +9,26 @@ import {{{.}}}
* {{{.}}}
{{/description}}
*/
-@Component
-@ConditionalOnProperty(name = ["{{topicPropertyKey}}"])
class {{name}}(
- private val kafkaTemplate: KafkaTemplate
+ private val kafkaTemplate: KafkaTemplate,
+ private val topic: String
) {
- private val log = LoggerFactory.getLogger({{name}}::class.java)
- {{=<% %>=}}
- @Value("\${<%topicPropertyKey%>}")
- private lateinit var topic: String
- <%={{ }}=%>
{{#sendMethods}}
- fun {{methodName}}(key: String, message: {{payloadType}}) {
- log.info("Sending {{payloadType}} to topic={}, key={}", topic, key)
- kafkaTemplate.send(ProducerRecord(topic, key, message))
+ fun {{methodName}}(
+ key: String,
+ message: {{payloadType}}{{#hasHeaders}},
+ headers: {{headerType}}{{/hasHeaders}}
+ ): CompletableFuture> {
+{{#hasHeaders}}
+ val record = ProducerRecord(topic, key, message)
+{{#headerProperties}}
+ headers.{{accessorName}}?.let { record.headers().add("{{name}}", it.toString().toByteArray(Charsets.UTF_8)) }
+{{/headerProperties}}
+ return kafkaTemplate.send(record)
+{{/hasHeaders}}
+{{^hasHeaders}}
+ return kafkaTemplate.send(ProducerRecord(topic, key, message))
+{{/hasHeaders}}
}
{{/sendMethods}}
}
diff --git a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-producer.mustache b/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-producer.mustache
deleted file mode 100644
index 8ec1dd77..00000000
--- a/asyncapi-generator-core/src/main/resources/kotlin/spring-kafka-simple-producer.mustache
+++ /dev/null
@@ -1,21 +0,0 @@
-package {{packageName}}
-
-{{#imports}}
-import {{{.}}}
-{{/imports}}
-
-/**
-{{#description}}
- * {{{.}}}
-{{/description}}
- */
-class {{name}}(
- private val kafkaTemplate: KafkaTemplate,
- private val topic: String
-) {
- {{#sendMethods}}
- fun {{methodName}}(key: String, message: {{payloadType}}) {
- kafkaTemplate.send(ProducerRecord(topic, key, message))
- }
- {{/sendMethods}}
-}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractJavaGeneratorClass.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractJavaGeneratorClass.kt
index a34035cc..ec5eab1c 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractJavaGeneratorClass.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractJavaGeneratorClass.kt
@@ -8,7 +8,6 @@ import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorOutp
import dev.banking.asyncapi.generator.core.generator.configuration.JavaModelType
import dev.banking.asyncapi.generator.core.generator.configuration.ModelGeneration
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import java.io.File
abstract class AbstractJavaGeneratorClass {
@@ -26,9 +25,8 @@ abstract class AbstractJavaGeneratorClass {
schemaPackage: String? = null,
generateModels: Boolean = true,
generateSpringKafkaClient: Boolean = false,
+ generateKafkaHeaders: Boolean = true,
generateQuarkusKafkaClient: Boolean = false,
- kafkaTopicsPropertyPrefix: String = "kafka.topics",
- springKafkaClientType: SpringKafkaClientType = SpringKafkaClientType.FULL,
modelAnnotation: String? = null,
javaModelType: JavaModelType = JavaModelType.CLASS,
): String {
@@ -56,11 +54,11 @@ abstract class AbstractJavaGeneratorClass {
buildList {
if (generateSpringKafkaClient) {
add(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = effectiveClientPackage,
modelPackageName = modelPackage,
- clientType = springKafkaClientType,
- topicPropertyPrefix = kafkaTopicsPropertyPrefix,
+ headers = ClientGeneration.Headers(enabled = generateKafkaHeaders),
+ springKafka = ClientGeneration.SpringKafka(),
),
)
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractKotlinGeneratorClass.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractKotlinGeneratorClass.kt
index 4856f7e6..1fe02c9c 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractKotlinGeneratorClass.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AbstractKotlinGeneratorClass.kt
@@ -7,7 +7,6 @@ import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorConf
import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorOutputConfiguration
import dev.banking.asyncapi.generator.core.generator.configuration.ModelGeneration
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import java.io.File
abstract class AbstractKotlinGeneratorClass {
@@ -25,9 +24,8 @@ abstract class AbstractKotlinGeneratorClass {
schemaPackage: String? = null,
generateModels: Boolean = true,
generateSpringKafkaClient: Boolean = false,
+ generateKafkaHeaders: Boolean = true,
generateQuarkusKafkaClient: Boolean = false,
- kafkaTopicsPropertyPrefix: String = "kafka.topics",
- springKafkaClientType: SpringKafkaClientType = SpringKafkaClientType.FULL,
modelAnnotation: String? = null,
): String {
val bundled = bundlerFixtures.bundledDocument(yaml)
@@ -53,11 +51,11 @@ abstract class AbstractKotlinGeneratorClass {
buildList {
if (generateSpringKafkaClient) {
add(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = effectiveClientPackage,
modelPackageName = modelPackage,
- clientType = springKafkaClientType,
- topicPropertyPrefix = kafkaTopicsPropertyPrefix,
+ headers = ClientGeneration.Headers(enabled = generateKafkaHeaders),
+ springKafka = ClientGeneration.SpringKafka(),
),
)
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AsyncApiGeneratorOutputContractTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AsyncApiGeneratorOutputContractTest.kt
index 0ae17d81..e2452abf 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AsyncApiGeneratorOutputContractTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/AsyncApiGeneratorOutputContractTest.kt
@@ -9,7 +9,6 @@ import dev.banking.asyncapi.generator.core.generator.configuration.GeneratorOutp
import dev.banking.asyncapi.generator.core.generator.configuration.ModelGeneration
import dev.banking.asyncapi.generator.core.generator.configuration.SchemaGeneration
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import dev.banking.asyncapi.generator.core.model.exceptions.AsyncApiGeneratorException
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
@@ -246,10 +245,10 @@ class AsyncApiGeneratorOutputContractTest {
resourceOutputDirectory = resourceOutputDirectory,
clients =
listOf(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = "com.example.kafka",
modelPackageName = "com.example.model",
- clientType = SpringKafkaClientType.SIMPLE,
+ springKafka = ClientGeneration.SpringKafka(),
),
),
),
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzerTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzerTest.kt
index 090dbff4..b5619c04 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzerTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/analyzer/ChannelAnalyzerTest.kt
@@ -83,6 +83,40 @@ class ChannelAnalyzerTest {
assertEquals(false, analyzed.isConsumer, "Should NOT be consumer")
}
+ @Test
+ fun `should analyze generated header type for messages with headers`() {
+ val channel = Channel(
+ messages = mapOf(
+ "UserSignup" to MessageInterface.MessageInline(
+ Message(
+ name = "UserSignup",
+ headers =
+ SchemaInterface.SchemaInline(
+ Schema(
+ type = "object",
+ properties =
+ mapOf(
+ "correlationId" to SchemaInterface.SchemaInline(Schema(type = "string")),
+ ),
+ ),
+ ),
+ payload = SchemaInterface.SchemaInline(Schema(title = "MyPayload", type = "object")),
+ ),
+ ),
+ ),
+ )
+ val doc = AsyncApiDocument(
+ asyncapi = "3.0.0",
+ info = Info("Title", "1.0"),
+ channels = mapOf("userEvents" to ChannelInterface.ChannelInline(channel)),
+ )
+
+ val analyzed = analyzer.analyze(doc).channels.single().messages.single()
+
+ assertEquals("TopicUserEventsHeadersUserSignup", analyzed.headers?.typeName)
+ assertEquals(listOf("correlationId"), analyzed.headers?.properties?.keys?.toList())
+ }
+
@Test
fun `should preserve inline multi format payload separately from asyncapi messages`() {
val avroSchema = nativeAvroSchema()
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactoryTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactoryTest.kt
index e40449f5..c011660e 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactoryTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationFactoryTest.kt
@@ -2,7 +2,6 @@ package dev.banking.asyncapi.generator.core.generator.configuration
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName.JAVA
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
@@ -78,18 +77,17 @@ class GeneratorConfigurationFactoryTest {
}
@Test
- fun `create enables Spring Kafka client generation when client type is configured`() {
+ fun `create enables Kafka and Spring Kafka client generation when client package is configured`() {
val configuration =
GeneratorConfigurationFactory.create(
request(
models = GeneratorConfigurationRequest.Models(packageName = "com.example.model"),
clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
- clientType = SpringKafkaClientType.SIMPLE,
- topicPropertyPrefix = "custom.topics",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
),
),
@@ -97,11 +95,10 @@ class GeneratorConfigurationFactoryTest {
assertEquals(
listOf(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = "com.example.client",
modelPackageName = "com.example.model",
- clientType = SpringKafkaClientType.SIMPLE,
- topicPropertyPrefix = "custom.topics",
+ springKafka = ClientGeneration.SpringKafka(),
),
),
configuration.clients,
@@ -115,10 +112,11 @@ class GeneratorConfigurationFactoryTest {
request(
clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
modelPackageName = "com.example.external.model",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
),
),
@@ -126,10 +124,49 @@ class GeneratorConfigurationFactoryTest {
assertEquals(
listOf(
- ClientGeneration.SpringKafka(
+ ClientGeneration.Kafka(
packageName = "com.example.client",
modelPackageName = "com.example.external.model",
- clientType = SpringKafkaClientType.SIMPLE,
+ springKafka = ClientGeneration.SpringKafka(),
+ ),
+ ),
+ configuration.clients,
+ )
+ }
+
+ @Test
+ fun `create maps kafka header and spring kafka generation options`() {
+ val configuration =
+ GeneratorConfigurationFactory.create(
+ request(
+ models = GeneratorConfigurationRequest.Models(packageName = "com.example.model"),
+ clients =
+ GeneratorConfigurationRequest.Clients(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
+ packageName = "com.example.client",
+ headers = GeneratorConfigurationRequest.KafkaHeaders(enabled = false),
+ springKafka =
+ GeneratorConfigurationRequest.KafkaSpringKafka(
+ producer = GeneratorConfigurationRequest.KafkaProducer(enabled = false),
+ consumer = GeneratorConfigurationRequest.KafkaConsumer(enabled = true),
+ ),
+ ),
+ ),
+ ),
+ )
+
+ assertEquals(
+ listOf(
+ ClientGeneration.Kafka(
+ packageName = "com.example.client",
+ modelPackageName = "com.example.model",
+ headers = ClientGeneration.Headers(enabled = false),
+ springKafka =
+ ClientGeneration.SpringKafka(
+ producer = ClientGeneration.Producer(enabled = false),
+ consumer = ClientGeneration.Consumer(enabled = true),
+ ),
),
),
configuration.clients,
@@ -219,14 +256,14 @@ class GeneratorConfigurationFactoryTest {
request(
clients =
GeneratorConfigurationRequest.Clients(
- springKafka = GeneratorConfigurationRequest.SpringKafka(),
+ kafka = GeneratorConfigurationRequest.Kafka(),
),
),
)
}
assertEquals(
- "clients.springKafka.packageName is required when clients.springKafka is configured",
+ "clients.kafka.packageName is required when clients.kafka is configured",
exception.message,
)
}
@@ -239,9 +276,10 @@ class GeneratorConfigurationFactoryTest {
request(
clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
),
),
@@ -249,7 +287,7 @@ class GeneratorConfigurationFactoryTest {
}
assertEquals(
- "clients.springKafka.modelPackageName is required when models.packageName is not configured",
+ "clients.kafka.modelPackageName is required when models.packageName is not configured",
exception.message,
)
}
@@ -329,27 +367,6 @@ class GeneratorConfigurationFactoryTest {
)
}
- @Test
- fun `create rejects blank Kafka topics property prefix`() {
- val exception =
- assertFailsWith {
- GeneratorConfigurationFactory.create(
- request(
- clients =
- GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
- packageName = "com.example.client",
- topicPropertyPrefix = "",
- ),
- ),
- ),
- )
- }
-
- assertEquals("clients.springKafka.topicPropertyPrefix cannot be empty", exception.message)
- }
-
@Test
fun `create rejects empty package names`() {
assertConfigurationError(
@@ -373,15 +390,16 @@ class GeneratorConfigurationFactoryTest {
),
)
assertConfigurationError(
- expectedMessage = "clients.springKafka.packageName cannot be empty",
+ expectedMessage = "clients.kafka.packageName cannot be empty",
request =
request(
clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
packageName = " ",
modelPackageName = "com.example.model",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
),
),
@@ -400,16 +418,17 @@ class GeneratorConfigurationFactoryTest {
)
assertConfigurationError(
expectedMessage =
- "clients.springKafka.modelPackageName must be a dot-separated package name, " +
+ "clients.kafka.modelPackageName must be a dot-separated package name, " +
"for example com.example.model",
request =
request(
clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- GeneratorConfigurationRequest.SpringKafka(
+ kafka =
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
modelPackageName = "com.example.",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
),
),
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequestTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequestTest.kt
index 99a80ed4..4ab60496 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequestTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/configuration/GeneratorConfigurationRequestTest.kt
@@ -1,6 +1,4 @@
package dev.banking.asyncapi.generator.core.generator.configuration
-
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
@@ -84,43 +82,63 @@ class GeneratorConfigurationRequestTest {
}
@Test
- fun `spring kafka request is created only when client output is configured`() {
- assertNull(GeneratorConfigurationRequest.springKafka())
+ fun `kafka request is created only when client output is configured`() {
+ assertNull(GeneratorConfigurationRequest.kafka())
assertNull(
- GeneratorConfigurationRequest.springKafka(
+ GeneratorConfigurationRequest.kafka(
enabled = false,
packageName = "com.example.client",
modelPackageName = "com.example.model",
- mode = "full",
- topicPropertyPrefix = "custom.topics",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
)
assertEquals(
- GeneratorConfigurationRequest.SpringKafka(
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
modelPackageName = "com.example.model",
- clientType = SpringKafkaClientType.FULL,
- topicPropertyPrefix = "custom.topics",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
- GeneratorConfigurationRequest.springKafka(
+ GeneratorConfigurationRequest.kafka(
packageName = "com.example.client",
modelPackageName = "com.example.model",
- mode = "full",
- topicPropertyPrefix = "custom.topics",
+ springKafka = GeneratorConfigurationRequest.KafkaSpringKafka(),
),
)
}
@Test
- fun `spring kafka request defaults to simple mode and default topic prefix`() {
+ fun `kafka request can be created from package only`() {
assertEquals(
- GeneratorConfigurationRequest.SpringKafka(
+ GeneratorConfigurationRequest.Kafka(
packageName = "com.example.client",
- clientType = SpringKafkaClientType.SIMPLE,
- topicPropertyPrefix = GeneratorConfigurationRequest.DEFAULT_KAFKA_TOPICS_PROPERTY_PREFIX,
),
- GeneratorConfigurationRequest.springKafka(packageName = "com.example.client"),
+ GeneratorConfigurationRequest.kafka(packageName = "com.example.client"),
+ )
+ }
+
+ @Test
+ fun `spring kafka request is created only when kafka spring kafka output is configured`() {
+ assertNull(GeneratorConfigurationRequest.kafkaSpringKafka())
+ assertNull(
+ GeneratorConfigurationRequest.kafkaSpringKafka(
+ enabled = false,
+ producer = GeneratorConfigurationRequest.KafkaProducer(enabled = false),
+ consumer = GeneratorConfigurationRequest.KafkaConsumer(enabled = false),
+ ),
+ )
+
+ assertEquals(
+ GeneratorConfigurationRequest.KafkaSpringKafka(),
+ GeneratorConfigurationRequest.kafkaSpringKafka(enabled = true),
+ )
+ assertEquals(
+ GeneratorConfigurationRequest.KafkaSpringKafka(
+ producer = GeneratorConfigurationRequest.KafkaProducer(enabled = false),
+ ),
+ GeneratorConfigurationRequest.kafkaSpringKafka(
+ producer = GeneratorConfigurationRequest.KafkaProducer(enabled = false),
+ ),
)
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/input/GenerationInputCompatibilityValidatorTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/input/GenerationInputCompatibilityValidatorTest.kt
index 67757331..c1b20a0c 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/input/GenerationInputCompatibilityValidatorTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/input/GenerationInputCompatibilityValidatorTest.kt
@@ -6,7 +6,6 @@ import dev.banking.asyncapi.generator.core.generator.configuration.JavaModelType
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
import dev.banking.asyncapi.generator.core.generator.plan.GenerationPlan
import dev.banking.asyncapi.generator.core.generator.plan.GenerationTask
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import dev.banking.asyncapi.generator.core.model.exceptions.AsyncApiGeneratorException
import dev.banking.asyncapi.generator.core.model.schemas.MultiFormatSchema
import dev.banking.asyncapi.generator.core.model.schemas.Schema
@@ -94,10 +93,8 @@ class GenerationInputCompatibilityValidatorTest {
listOf(
GenerationTask.SpringKafkaClient(
language = GeneratorName.KOTLIN,
- clientType = SpringKafkaClientType.SIMPLE,
clientPackage = "com.example.kafka",
modelPackage = "com.example.model",
- topicPropertyPrefix = "kafka.topics",
),
),
),
@@ -113,10 +110,8 @@ class GenerationInputCompatibilityValidatorTest {
listOf(
GenerationTask.SpringKafkaClient(
language = GeneratorName.KOTLIN,
- clientType = SpringKafkaClientType.SIMPLE,
clientPackage = "com.example.kafka",
modelPackage = "com.example.model",
- topicPropertyPrefix = "kafka.topics",
),
),
),
@@ -140,10 +135,8 @@ class GenerationInputCompatibilityValidatorTest {
listOf(
GenerationTask.SpringKafkaClient(
language = GeneratorName.KOTLIN,
- clientType = SpringKafkaClientType.SIMPLE,
clientPackage = "com.example.kafka",
modelPackage = "com.example.model",
- topicPropertyPrefix = "kafka.topics",
),
),
),
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaPrimitivePayloadTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaPrimitivePayloadTest.kt
index 2aae20f5..e5e01d76 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaPrimitivePayloadTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaPrimitivePayloadTest.kt
@@ -35,8 +35,6 @@ class GenerateJavaPrimitivePayloadTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val producerFile =
@@ -44,7 +42,7 @@ class GenerateJavaPrimitivePayloadTest {
packageName.replace(
'.',
'/'
- ) + "/producer/TopicSimpleTopicProducerSimpleStringMessage.java"
+ ) + "/producer/SimpleTopicProducerSimpleStringMessage.java"
)
assertTrue(producerFile.exists(), "Producer should be generated")
val producerContent = producerFile.readText()
@@ -52,6 +50,10 @@ class GenerateJavaPrimitivePayloadTest {
producerContent.contains("KafkaTemplate"),
"Producer should use typed KafkaTemplate for single payload",
)
+ assertTrue(
+ producerContent.contains("CompletableFuture>"),
+ "Producer should return the Spring Kafka send result future",
+ )
}
@Test
@@ -85,14 +87,12 @@ class GenerateJavaPrimitivePayloadTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val producerFileA =
- outputDir.resolve(packageName.replace('.', '/') + "/producer/TopicMultiTopicProducerStringMessage.java")
+ outputDir.resolve(packageName.replace('.', '/') + "/producer/MultiTopicProducerStringMessage.java")
val producerFileB =
- outputDir.resolve(packageName.replace('.', '/') + "/producer/TopicMultiTopicProducerIntMessage.java")
+ outputDir.resolve(packageName.replace('.', '/') + "/producer/MultiTopicProducerIntMessage.java")
assertTrue(producerFileA.exists(), "StringMessage producer should be generated")
assertTrue(producerFileB.exists(), "IntMessage producer should be generated")
val producerContentA = producerFileA.readText()
@@ -101,9 +101,17 @@ class GenerateJavaPrimitivePayloadTest {
producerContentA.contains("KafkaTemplate"),
"StringMessage producer should use typed KafkaTemplate",
)
+ assertTrue(
+ producerContentA.contains("CompletableFuture>"),
+ "StringMessage producer should return the Spring Kafka send result future",
+ )
assertTrue(
producerContentB.contains("KafkaTemplate"),
"IntMessage producer should use typed KafkaTemplate",
)
+ assertTrue(
+ producerContentB.contains("CompletableFuture>"),
+ "IntMessage producer should return the Spring Kafka send result future",
+ )
}
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaClientTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaClientTest.kt
deleted file mode 100644
index 7c6be337..00000000
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaClientTest.kt
+++ /dev/null
@@ -1,237 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.java.kafka
-
-import dev.banking.asyncapi.generator.core.generator.AbstractJavaGeneratorClass
-import org.junit.jupiter.api.Test
-import java.io.File
-import kotlin.test.assertTrue
-
-class GenerateJavaSpringKafkaClientTest : AbstractJavaGeneratorClass() {
- @Test
- fun `should generate full spring kafka ecosystem for Java`() {
- val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val modelPath = "dev/banking/test/userservice/v1/model"
- val clientPath = "dev/banking/test/userservice/v1/client"
-
- val modelDir = outputDir.resolve(modelPath)
- assertTrue(modelDir.resolve("UserSignedUpPayload.java").exists(), "Model UserSignedUpPayload missing")
- assertTrue(modelDir.resolve("UserLoggedInPayload.java").exists(), "Model UserLoggedInPayload missing")
-
- val clientDir = outputDir.resolve(clientPath)
- val autoconfigDir = clientDir.resolve("config")
- val handlerDir = clientDir.resolve("handler")
- val listenerDir = clientDir.resolve("listener")
- val producerDir = clientDir.resolve("producer")
- assertTrue(
- listenerDir.resolve("TopicUserEventsListenerUserSignedUp.java").exists(),
- "UserSignedUp Listener missing",
- )
- assertTrue(
- handlerDir.resolve("TopicUserEventsHandlerUserSignedUp.java").exists(),
- "UserSignedUp Handler missing",
- )
- assertTrue(
- listenerDir.resolve("TopicUserEventsListenerUserLoggedIn.java").exists(),
- "UserLoggedIn Listener missing",
- )
- assertTrue(
- handlerDir.resolve("TopicUserEventsHandlerUserLoggedIn.java").exists(),
- "UserLoggedIn Handler missing",
- )
- assertTrue(producerDir.resolve("TopicUserEventsProducerUserSignedUp.java").exists(), "UserSignedUp Producer missing")
- assertTrue(producerDir.resolve("TopicUserEventsProducerUserLoggedIn.java").exists(), "UserLoggedIn Producer missing")
- val userSignedUpListenerContent = listenerDir.resolve("TopicUserEventsListenerUserSignedUp.java").readText()
- assertTrue(
- userSignedUpListenerContent.contains("ConsumerRecord"),
- "Listener should be typed to UserSignedUp",
- )
- assertTrue(userSignedUpListenerContent.contains("import $modelPackage.UserSignedUpPayload;"), "Import missing")
- assertTrue(
- userSignedUpListenerContent.contains("import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;"),
- "Missing ConditionalOnBean import",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@ConditionalOnBean(TopicUserEventsHandlerUserSignedUp.class)"),
- "Missing @ConditionalOnBean annotation",
- )
- val userProducerContent = producerDir.resolve("TopicUserEventsProducerUserSignedUp.java").readText()
- assertTrue(
- userProducerContent.contains("@ConditionalOnProperty(name = \"kafka.topics.userEvents\")"),
- "Missing @ConditionalOnProperty annotation",
- )
- assertTrue(
- userProducerContent.contains("@Value(\"\${kafka.topics.userEvents}\")"),
- "Producer should read topic from kafka.topics.userEvents",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@ConditionalOnProperty(name = \"kafka.topics.userEvents\")"),
- "Listener should be conditional on topic property",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@KafkaListener(topics = \"\${kafka.topics.userEvents}\""),
- "Listener should read topic from kafka.topics.userEvents",
- )
- assertTrue(
- userSignedUpListenerContent.contains("groupId = \"\${spring.kafka.consumer.group-id}\""),
- "Listener should use Spring groupId pltestholder",
- )
-
- val autoConfigContent = autoconfigDir.resolve("AsyncApiKafkaAutoConfiguration.java").readText()
- assertTrue(autoConfigContent.contains("@ComponentScan"), "Auto-configuration should include ComponentScan")
- assertTrue(
- autoConfigContent.contains("basePackages = \"$clientPackage\""),
- "Auto-configuration should scan the client package",
- )
-
- val importsFile =
- File("target/generated-resources/asyncapi/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")
- assertTrue(importsFile.exists(), "Auto-configuration imports file should be generated")
- val importsContent = importsFile.readText()
- assertTrue(
- importsContent.contains("$clientPackage.config.AsyncApiKafkaAutoConfiguration"),
- "Auto-configuration imports should include generated config",
- )
- }
-
- @Test
- fun `should apply custom topic property prefix for Java`() {
- val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
- val outputDir = File("target/generated-sources/asyncapi")
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- codegenOutputDirectory = outputDir,
- kafkaTopicsPropertyPrefix = "my.property",
- )
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val producerDir = clientDir.resolve("producer")
- val listenerDir = clientDir.resolve("listener")
- val producerContent = producerDir.resolve("TopicUserEventsProducerUserSignedUp.java").readText()
- val listenerContent = listenerDir.resolve("TopicUserEventsListenerUserSignedUp.java").readText()
- assertTrue(
- producerContent.contains("@Value(\"\${my.property.userEvents}\")"),
- "Producer should use custom topic property key",
- )
- assertTrue(
- listenerContent.contains("@KafkaListener(topics = \"\${my.property.userEvents}\""),
- "Listener should use custom topic property key",
- )
- }
-
- @Test
- fun `should generate full spring kafka client with native avro payload type for Java`() {
- val yaml = File("src/test/resources/generator/asyncapi_native_avro_spring_kafka_client.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreated.java").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreated.java").readText()
-
- assertTrue(listenerContent.contains("import com.example.avro.UserCreated;"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.avro.UserCreated;"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate full spring kafka client with external native avro payload type for Java`() {
- val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreatedAvro.java").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreatedAvro.java").readText()
-
- assertTrue(listenerContent.contains("import com.example.external.avro.UserCreatedAvro;"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.external.avro.UserCreatedAvro;"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate full spring kafka client with native protobuf payload type for Java`() {
- val yaml = File("src/test/resources/generator/asyncapi_native_protobuf_spring_kafka_client.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreated.java").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreated.java").readText()
-
- assertTrue(listenerContent.contains("import com.example.protobuf.UserCreated;"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.protobuf.UserCreated;"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate full spring kafka client with external native protobuf payload type for Java`() {
- val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreatedProtobuf.java").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreatedProtobuf.java").readText()
-
- assertTrue(listenerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf;"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf;"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaOpenPayloadClientTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaOpenPayloadClientTest.kt
index 9bd55837..c17d0db3 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaOpenPayloadClientTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaOpenPayloadClientTest.kt
@@ -1,7 +1,6 @@
package dev.banking.asyncapi.generator.core.generator.java.kafka
import dev.banking.asyncapi.generator.core.generator.AbstractJavaGeneratorClass
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertFalse
@@ -23,41 +22,6 @@ class GenerateJavaSpringKafkaOpenPayloadClientTest : AbstractJavaGeneratorClass(
generateSpringKafkaClient = true,
)
- val outputDir = File("target/generated-sources/asyncapi")
- val modelDir = outputDir.resolve("dev/banking/test/dlq/model")
- val handlerDir = outputDir.resolve("dev/banking/test/dlq/client/handler")
- val listenerDir = outputDir.resolve("dev/banking/test/dlq/client/listener")
- val producerDir = outputDir.resolve("dev/banking/test/dlq/client/producer")
-
- val modelFile = modelDir.resolve("DeadLetterQueueEventPayload.java")
- assertFalse(modelFile.exists(), "Open payload should not generate a model class")
-
- val handlerContent = handlerDir.resolve("TopicUserDlqHandlerDeadLetterQueueEvent.java").readText()
- assertTrue(handlerContent.contains("ConsumerRecord"))
-
- val listenerContent = listenerDir.resolve("TopicUserDlqListenerDeadLetterQueueEvent.java").readText()
- assertTrue(listenerContent.contains("ConsumerRecord"))
-
- val producerContent = producerDir.resolve("TopicUserDlqProducerDeadLetterQueueEvent.java").readText()
- assertTrue(producerContent.contains("KafkaTemplate"))
- assertTrue(producerContent.contains("void sendDeadLetterQueueEvent"))
- }
-
- @Test
- fun `should use Object for open payload in spring kafka simple clients`() {
- val yaml = File("src/test/resources/generator/asyncapi_open_payload_kafka_inline.yaml")
- val modelPackage = "dev.banking.test.dlq.model"
- val clientPackage = "dev.banking.test.dlq.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
val outputDir = File("target/generated-sources/asyncapi")
val modelDir = outputDir.resolve("dev/banking/test/dlq/model")
val producerDir = outputDir.resolve("dev/banking/test/dlq/client/producer")
@@ -68,7 +32,7 @@ class GenerateJavaSpringKafkaOpenPayloadClientTest : AbstractJavaGeneratorClass(
val producerContent = producerDir.resolve("UserDlqProducerDeadLetterQueueEvent.java").readText()
assertTrue(producerContent.contains("KafkaTemplate"))
- assertTrue(producerContent.contains("void sendDeadLetterQueueEvent"))
+ assertTrue(producerContent.contains("CompletableFuture> sendDeadLetterQueueEvent"))
val consumerContent = consumerDir.resolve("UserDlqConsumer.java").readText()
assertTrue(consumerContent.contains("ConsumerRecord"))
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaSimpleTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaTest.kt
similarity index 55%
rename from asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaSimpleTest.kt
rename to asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaTest.kt
index 02e91da5..c858ebd2 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaSimpleTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/java/kafka/GenerateJavaSpringKafkaTest.kt
@@ -1,15 +1,14 @@
package dev.banking.asyncapi.generator.core.generator.java.kafka
import dev.banking.asyncapi.generator.core.generator.AbstractJavaGeneratorClass
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertFalse
import kotlin.test.assertTrue
-class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
+class GenerateJavaSpringKafkaTest : AbstractJavaGeneratorClass() {
@Test
- fun `should generate simple spring kafka client for Java`() {
+ fun `should generate spring kafka client for Java`() {
val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -20,7 +19,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = true,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -29,24 +27,27 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
val consumerDir = outputDir.resolve("$clientPath/consumer")
val producerFile = producerDir.resolve("UserEventsProducerUserSignedUp.java")
- assertTrue(producerFile.exists(), "Simple producer should be generated")
+ assertTrue(producerFile.exists(), "Producer should be generated")
val producerContent = producerFile.readText()
assertTrue(producerContent.contains("class UserEventsProducerUserSignedUp"))
assertTrue(producerContent.contains("KafkaTemplate"))
assertTrue(producerContent.contains("sendUserSignedUp"))
- assertTrue(!producerContent.contains("@Component"), "Simple producer should not be annotated")
+ assertTrue(producerContent.contains("CompletableFuture>"))
+ assertTrue(producerContent.contains("return kafkaTemplate.send"))
+ assertTrue(!producerContent.contains("@Component"), "Producer should not be annotated")
val consumerFile = consumerDir.resolve("UserEventsConsumer.java")
- assertTrue(consumerFile.exists(), "Simple consumer should be generated")
+ assertTrue(consumerFile.exists(), "Consumer should be generated")
val consumerContent = consumerFile.readText()
assertTrue(consumerContent.contains("interface UserEventsConsumer"))
- assertTrue(consumerContent.contains("default void onUserSignedUp"))
+ assertTrue(consumerContent.contains("void onUserSignedUp"))
+ assertFalse(consumerContent.contains("default void"), "Consumer methods should be abstract")
assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(!consumerContent.contains("@KafkaListener"), "Simple consumer should not be annotated")
+ assertTrue(!consumerContent.contains("@KafkaListener"), "Consumer should not be annotated")
}
@Test
- fun `should not generate header classes for spring kafka simple client in Java`() {
+ fun `should generate header classes for spring kafka client in Java`() {
val yaml = File("src/test/resources/generator/asyncapi_message_headers.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -57,16 +58,81 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = true,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
+ val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
val headerDir = outputDir.resolve("dev/banking/test/userservice/v1/client/header")
- assertFalse(headerDir.exists(), "Simple spring kafka client should not generate header classes")
+ assertTrue(headerDir.exists(), "Spring Kafka client should generate header classes")
+
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.java").readText()
+ assertTrue(consumerContent.contains("import dev.banking.test.userservice.v1.client.header.TopicUserEventsHeadersUserSignup;"))
+ assertTrue(
+ consumerContent.contains(
+ "void onUserSignup(ConsumerRecord record, " +
+ "TopicUserEventsHeadersUserSignup headers);",
+ ),
+ )
+ assertFalse(consumerContent.contains("default void"), "Consumer methods should be abstract")
+
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserSignup.java").readText()
+ assertTrue(producerContent.contains("import dev.banking.test.userservice.v1.client.header.TopicUserEventsHeadersUserSignup;"))
+ assertTrue(producerContent.contains("import java.util.concurrent.CompletableFuture;"))
+ assertTrue(producerContent.contains("import org.springframework.kafka.support.SendResult;"))
+ assertTrue(producerContent.contains("import java.nio.charset.StandardCharsets;"))
+ assertTrue(producerContent.contains("CompletableFuture> sendUserSignup("))
+ assertTrue(producerContent.contains("UserSignupPayload message"))
+ assertTrue(producerContent.contains("TopicUserEventsHeadersUserSignup headers"))
+ assertTrue(
+ producerContent.contains(
+ "record.headers().add(\"correlationId\", " +
+ "String.valueOf(headers.getCorrelationId()).getBytes(StandardCharsets.UTF_8));",
+ ),
+ )
+ }
+
+ @Test
+ fun `should not reference typed headers when Kafka header generation is disabled for Java`() {
+ val yaml = File("src/test/resources/generator/asyncapi_message_headers.yaml")
+ val modelPackage = "dev.banking.test.userservice.v1.model"
+ val clientPackage = "dev.banking.test.userservice.v1.client"
+ val outputDir = File("target/generated-sources/asyncapi-java-spring-kafka-no-headers")
+ val resourceOutputDirectory = File("target/generated-resources/asyncapi-java-spring-kafka-no-headers")
+ outputDir.deleteRecursively()
+ resourceOutputDirectory.deleteRecursively()
+
+ generateElement(
+ yaml = yaml,
+ codegenOutputDirectory = outputDir,
+ resourceOutputDirectory = resourceOutputDirectory,
+ modelPackage = modelPackage,
+ clientPackage = clientPackage,
+ generateModels = true,
+ generateSpringKafkaClient = true,
+ generateKafkaHeaders = false,
+ )
+
+ val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
+ val headerDir = clientDir.resolve("header")
+ assertFalse(headerDir.exists(), "Header classes should not be generated when Kafka headers are disabled")
+
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.java").readText()
+ assertFalse(consumerContent.contains(".client.header."))
+ assertFalse(consumerContent.contains("TopicUserEventsHeadersUserSignup"))
+ assertTrue(consumerContent.contains("void onUserSignup(ConsumerRecord record);"))
+ assertFalse(consumerContent.contains("default void"), "Consumer methods should be abstract")
+
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserSignup.java").readText()
+ assertFalse(producerContent.contains(".client.header."))
+ assertFalse(producerContent.contains("TopicUserEventsHeadersUserSignup"))
+ assertFalse(producerContent.contains("StandardCharsets"))
+ assertFalse(producerContent.contains("record.headers().add"))
+ assertTrue(producerContent.contains("CompletableFuture> sendUserSignup("))
+ assertTrue(producerContent.contains("UserSignupPayload message"))
}
@Test
- fun `should generate simple spring kafka client with native avro payload type for Java`() {
+ fun `should generate spring kafka client with native avro payload type for Java`() {
val yaml = File("src/test/resources/generator/asyncapi_native_avro_spring_kafka_client.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -77,7 +143,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = false,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -89,10 +154,11 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.avro.UserCreated;"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate simple spring kafka client with external native avro payload type for Java`() {
+ fun `should generate spring kafka client with external native avro payload type for Java`() {
val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -103,7 +169,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = false,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -115,10 +180,11 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.external.avro.UserCreatedAvro;"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate simple spring kafka client with native protobuf payload type for Java`() {
+ fun `should generate spring kafka client with native protobuf payload type for Java`() {
val yaml = File("src/test/resources/generator/asyncapi_native_protobuf_spring_kafka_client.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -129,7 +195,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = false,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -141,10 +206,11 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.protobuf.UserCreated;"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate simple spring kafka client with external native protobuf payload type for Java`() {
+ fun `should generate spring kafka client with external native protobuf payload type for Java`() {
val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -155,7 +221,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
clientPackage = clientPackage,
generateModels = false,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -167,5 +232,6 @@ class GenerateJavaSpringKafkaSimpleTest : AbstractJavaGeneratorClass() {
assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf;"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGenerationTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGenerationTest.kt
index cf7da83e..e315ee64 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGenerationTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kafka/spring/SpringKafkaClientGenerationTest.kt
@@ -3,10 +3,10 @@ package dev.banking.asyncapi.generator.core.generator.kafka.spring
import dev.banking.asyncapi.generator.core.fixtures.GenerationInputFixtures
import dev.banking.asyncapi.generator.core.generator.model.GeneratorName
import dev.banking.asyncapi.generator.core.generator.plan.GenerationTask
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
+import kotlin.test.assertFalse
import kotlin.test.assertTrue
class SpringKafkaClientGenerationTest {
@@ -17,7 +17,7 @@ class SpringKafkaClientGenerationTest {
lateinit var tempDir: Path
@Test
- fun `generate delegates Kotlin simple client task to Kotlin simple generator`() {
+ fun `generate delegates Kotlin client task to Kotlin generator`() {
val sourceOutputDirectory = tempDir.resolve("kotlin-sources").toFile()
val resourceOutputDirectory = tempDir.resolve("kotlin-resources").toFile()
@@ -25,7 +25,6 @@ class SpringKafkaClientGenerationTest {
task =
springKafkaClientTask(
language = GeneratorName.KOTLIN,
- clientType = SpringKafkaClientType.SIMPLE,
),
generationInput = fixtures.generationInputWithUserSignupChannel(),
sourceOutputDirectory = sourceOutputDirectory,
@@ -41,50 +40,7 @@ class SpringKafkaClientGenerationTest {
}
@Test
- fun `generate delegates Kotlin full client task to Kotlin full generator`() {
- val sourceOutputDirectory = tempDir.resolve("kotlin-full-sources").toFile()
- val resourceOutputDirectory = tempDir.resolve("kotlin-full-resources").toFile()
-
- generator.generate(
- task =
- springKafkaClientTask(
- language = GeneratorName.KOTLIN,
- clientType = SpringKafkaClientType.FULL,
- topicPropertyPrefix = "custom.topics",
- ),
- generationInput = fixtures.generationInputWithUserSignupChannel(),
- sourceOutputDirectory = sourceOutputDirectory,
- resourceOutputDirectory = resourceOutputDirectory,
- )
-
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/config/AsyncApiKafkaAutoConfiguration.kt").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/listener/TopicUserEventsListenerUserSignedUp.kt").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/handler/TopicUserEventsHandlerUserSignedUp.kt").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/producer/TopicUserEventsProducerUserSignedUp.kt").exists(),
- )
- val producerContent =
- sourceOutputDirectory
- .resolve("com/example/client/producer/TopicUserEventsProducerUserSignedUp.kt")
- .readText()
- assertTrue(
- producerContent.contains("custom.topics.userEvents"),
- )
- assertTrue(
- resourceOutputDirectory
- .resolve("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")
- .exists(),
- )
- }
-
- @Test
- fun `generate delegates Java simple client task to Java simple generator`() {
+ fun `generate delegates Java client task to Java generator`() {
val sourceOutputDirectory = tempDir.resolve("java-sources").toFile()
val resourceOutputDirectory = tempDir.resolve("java-resources").toFile()
@@ -92,7 +48,6 @@ class SpringKafkaClientGenerationTest {
task =
springKafkaClientTask(
language = GeneratorName.JAVA,
- clientType = SpringKafkaClientType.SIMPLE,
),
generationInput = fixtures.generationInputWithUserSignupChannel(),
sourceOutputDirectory = sourceOutputDirectory,
@@ -108,50 +63,41 @@ class SpringKafkaClientGenerationTest {
}
@Test
- fun `generate delegates Java full client task to Java full generator`() {
- val sourceOutputDirectory = tempDir.resolve("java-full-sources").toFile()
- val resourceOutputDirectory = tempDir.resolve("java-full-resources").toFile()
+ fun `generate respects producer and consumer task options`() {
+ val sourceOutputDirectory = tempDir.resolve("configured-sources").toFile()
+ val resourceOutputDirectory = tempDir.resolve("configured-resources").toFile()
generator.generate(
task =
springKafkaClientTask(
- language = GeneratorName.JAVA,
- clientType = SpringKafkaClientType.FULL,
+ language = GeneratorName.KOTLIN,
+ generateProducers = false,
+ generateConsumers = true,
),
generationInput = fixtures.generationInputWithUserSignupChannel(),
sourceOutputDirectory = sourceOutputDirectory,
resourceOutputDirectory = resourceOutputDirectory,
)
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/config/AsyncApiKafkaAutoConfiguration.java").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/listener/TopicUserEventsListenerUserSignedUp.java").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/handler/TopicUserEventsHandlerUserSignedUp.java").exists(),
- )
- assertTrue(
- sourceOutputDirectory.resolve("com/example/client/producer/TopicUserEventsProducerUserSignedUp.java").exists(),
+ assertFalse(
+ sourceOutputDirectory.resolve("com/example/client/producer/UserEventsProducerUserSignedUp.kt").exists(),
)
assertTrue(
- resourceOutputDirectory
- .resolve("META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")
- .exists(),
+ sourceOutputDirectory.resolve("com/example/client/consumer/UserEventsConsumer.kt").exists(),
)
}
private fun springKafkaClientTask(
language: GeneratorName,
- clientType: SpringKafkaClientType,
- topicPropertyPrefix: String = "kafka.topics",
+ generateProducers: Boolean = true,
+ generateConsumers: Boolean = true,
): GenerationTask.SpringKafkaClient =
GenerationTask.SpringKafkaClient(
language = language,
- clientType = clientType,
clientPackage = "com.example.client",
modelPackage = "com.example.model",
- topicPropertyPrefix = topicPropertyPrefix,
+ generateHeaders = true,
+ generateProducers = generateProducers,
+ generateConsumers = generateConsumers,
)
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOpenPayloadClientTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOpenPayloadClientTest.kt
index c7eb3d91..e15ec660 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOpenPayloadClientTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOpenPayloadClientTest.kt
@@ -1,13 +1,11 @@
package dev.banking.asyncapi.generator.core.generator.kotlin.kafka
import dev.banking.asyncapi.generator.core.generator.AbstractKotlinGeneratorClass
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertTrue
class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorClass() {
-
@Test
fun `should use typealias for open payload in spring kafka clients`() {
val yaml = File("src/test/resources/generator/asyncapi_open_payload_kafka.yaml")
@@ -22,44 +20,6 @@ class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorCl
generateSpringKafkaClient = true,
)
- val outputDir = File("target/generated-sources/asyncapi")
- val modelDir = outputDir.resolve("dev/banking/test/dlq/model")
- val handlerDir = outputDir.resolve("dev/banking/test/dlq/client/handler")
- val listenerDir = outputDir.resolve("dev/banking/test/dlq/client/listener")
- val producerDir = outputDir.resolve("dev/banking/test/dlq/client/producer")
-
- val modelFile = modelDir.resolve("DeadLetterQueueEvent.kt")
- assertTrue(modelFile.exists(), "DeadLetterQueueEvent typealias should be generated")
-
- val handlerContent = handlerDir.resolve("TopicUserDlqHandlerDeadLetterQueueEvent.kt").readText()
- assertTrue(handlerContent.contains("ConsumerRecord"))
- assertTrue(handlerContent.contains("import $modelPackage.DeadLetterQueueEvent"))
-
- val listenerContent = listenerDir.resolve("TopicUserDlqListenerDeadLetterQueueEvent.kt").readText()
- assertTrue(listenerContent.contains("ConsumerRecord"))
- assertTrue(listenerContent.contains("import $modelPackage.DeadLetterQueueEvent"))
-
- val producerContent = producerDir.resolve("TopicUserDlqProducerDeadLetterQueueEvent.kt").readText()
- assertTrue(producerContent.contains("KafkaTemplate"))
- assertTrue(producerContent.contains("fun sendDeadLetterQueueEvent"))
- assertTrue(producerContent.contains("import $modelPackage.DeadLetterQueueEvent"))
- }
-
- @Test
- fun `should use typealias for open payload in spring kafka simple clients`() {
- val yaml = File("src/test/resources/generator/asyncapi_open_payload_kafka.yaml")
- val modelPackage = "dev.banking.test.dlq.model"
- val clientPackage = "dev.banking.test.dlq.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
val outputDir = File("target/generated-sources/asyncapi")
val modelDir = outputDir.resolve("dev/banking/test/dlq/model")
val producerDir = outputDir.resolve("dev/banking/test/dlq/client/producer")
@@ -71,6 +31,7 @@ class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorCl
val producerContent = producerDir.resolve("UserDlqProducerDeadLetterQueueEvent.kt").readText()
assertTrue(producerContent.contains("KafkaTemplate"))
assertTrue(producerContent.contains("fun sendDeadLetterQueueEvent"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
assertTrue(producerContent.contains("import $modelPackage.DeadLetterQueueEvent"))
val consumerContent = consumerDir.resolve("UserDlqConsumer.kt").readText()
@@ -80,7 +41,7 @@ class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorCl
}
@Test
- fun `should use typealias for open payload inline in spring kafka simple clients`() {
+ fun `should use typealias for open payload inline in spring kafka clients`() {
val yaml = File("src/test/resources/generator/asyncapi_open_payload_kafka_inline.yaml")
val modelPackage = "dev.banking.test.dlq.model"
val clientPackage = "dev.banking.test.dlq.client"
@@ -91,7 +52,6 @@ class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorCl
clientPackage = clientPackage,
generateModels = true,
generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
)
val outputDir = File("target/generated-sources/asyncapi")
@@ -105,6 +65,7 @@ class GenerateKotlinSpringKafkaOpenPayloadClientTest : AbstractKotlinGeneratorCl
val producerContent = producerDir.resolve("UserDlqProducerDeadLetterQueueEvent.kt").readText()
assertTrue(producerContent.contains("KafkaTemplate"))
assertTrue(producerContent.contains("fun sendDeadLetterQueueEvent"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
assertTrue(producerContent.contains("import $modelPackage.DeadLetterQueueEventPayload"))
val consumerContent = consumerDir.resolve("UserDlqConsumer.kt").readText()
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOperationsTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOperationsTest.kt
index 3bd1e3a9..8feb7773 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOperationsTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaOperationsTest.kt
@@ -38,28 +38,22 @@ class GenerateKotlinSpringKafkaOperationsTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val packagePath = packageName.replace('.', '/')
assertTrue(
- outputDir.resolve("$packagePath/producer/TopicEventsProducerTestEvent.kt").exists(),
+ outputDir.resolve("$packagePath/producer/EventsProducerTestEvent.kt").exists(),
"Producer should exist"
)
assertFalse(
- outputDir.resolve("$packagePath/listener/TopicEventsListenerTestEvent.kt").exists(),
- "Listener should NOT exist"
- )
- assertFalse(
- outputDir.resolve("$packagePath/handler/TopicEventsHandlerTestEvent.kt").exists(),
- "Handler should NOT exist"
+ outputDir.resolve("$packagePath/consumer/EventsConsumer.kt").exists(),
+ "Consumer should NOT exist"
)
}
@Test
- fun `should generate ONLY listener when isConsumer=true`() {
+ fun `should generate ONLY consumer when isConsumer=true`() {
outputDir.deleteRecursively()
val channel =
@@ -76,23 +70,17 @@ class GenerateKotlinSpringKafkaOperationsTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val packagePath = packageName.replace('.', '/')
assertFalse(
- outputDir.resolve("$packagePath/producer/TopicEventsProducerTestEvent.kt").exists(),
+ outputDir.resolve("$packagePath/producer/EventsProducerTestEvent.kt").exists(),
"Producer should NOT exist"
)
assertTrue(
- outputDir.resolve("$packagePath/listener/TopicEventsListenerTestEvent.kt").exists(),
- "Listener should exist"
- )
- assertTrue(
- outputDir.resolve("$packagePath/handler/TopicEventsHandlerTestEvent.kt").exists(),
- "Handler should exist"
+ outputDir.resolve("$packagePath/consumer/EventsConsumer.kt").exists(),
+ "Consumer should exist"
)
}
@@ -114,19 +102,17 @@ class GenerateKotlinSpringKafkaOperationsTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val packagePath = packageName.replace('.', '/')
assertTrue(
- outputDir.resolve("$packagePath/producer/TopicEventsProducerTestEvent.kt").exists(),
+ outputDir.resolve("$packagePath/producer/EventsProducerTestEvent.kt").exists(),
"Producer should exist"
)
assertTrue(
- outputDir.resolve("$packagePath/listener/TopicEventsListenerTestEvent.kt").exists(),
- "Listener should exist"
+ outputDir.resolve("$packagePath/consumer/EventsConsumer.kt").exists(),
+ "Consumer should exist"
)
}
@@ -148,19 +134,17 @@ class GenerateKotlinSpringKafkaOperationsTest {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val packagePath = packageName.replace('.', '/')
assertFalse(
- outputDir.resolve("$packagePath/producer/TopicEventsProducerTestEvent.kt").exists(),
+ outputDir.resolve("$packagePath/producer/EventsProducerTestEvent.kt").exists(),
"Producer should NOT exist"
)
assertFalse(
- outputDir.resolve("$packagePath/listener/TopicEventsListenerTestEvent.kt").exists(),
- "Listener should NOT exist"
+ outputDir.resolve("$packagePath/consumer/EventsConsumer.kt").exists(),
+ "Consumer should NOT exist"
)
}
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaSimpleTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaSimpleTest.kt
deleted file mode 100644
index 7462c55a..00000000
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaSimpleTest.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.kotlin.kafka
-
-import dev.banking.asyncapi.generator.core.generator.AbstractKotlinGeneratorClass
-import dev.banking.asyncapi.generator.core.generator.plan.SpringKafkaClientType
-import org.junit.jupiter.api.Test
-import java.io.File
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-class GenerateKotlinSpringKafkaSimpleTest : AbstractKotlinGeneratorClass() {
- @Test
- fun `should generate simple spring kafka client`() {
- val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientPath = "dev/banking/test/userservice/v1/client"
- val producerDir = outputDir.resolve("$clientPath/producer")
- val consumerDir = outputDir.resolve("$clientPath/consumer")
-
- val producerFile = producerDir.resolve("UserEventsProducerUserSignedUp.kt")
- assertTrue(producerFile.exists(), "Simple producer should be generated")
- val producerContent = producerFile.readText()
- assertTrue(producerContent.contains("class UserEventsProducerUserSignedUp"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- assertTrue(producerContent.contains("sendUserSignedUp"))
- assertTrue(!producerContent.contains("@Component"), "Simple producer should not be annotated")
-
- val consumerFile = consumerDir.resolve("UserEventsConsumer.kt")
- assertTrue(consumerFile.exists(), "Simple consumer should be generated")
- val consumerContent = consumerFile.readText()
- assertTrue(consumerContent.contains("interface UserEventsConsumer"))
- assertTrue(consumerContent.contains("fun onUserSignedUp"))
- assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(!consumerContent.contains("@KafkaListener"), "Simple consumer should not be annotated")
- }
-
- @Test
- fun `should not generate header classes for spring kafka simple client`() {
- val yaml = File("src/test/resources/generator/asyncapi_message_headers.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = true,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val headerDir = outputDir.resolve("dev/banking/test/userservice/v1/client/header")
- assertFalse(headerDir.exists(), "Simple spring kafka client should not generate header classes")
- }
-
- @Test
- fun `should generate simple spring kafka client with native avro payload type`() {
- val yaml = File("src/test/resources/generator/asyncapi_native_avro_spring_kafka_client.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
- val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreated.kt").readText()
-
- assertTrue(consumerContent.contains("import com.example.avro.UserCreated"))
- assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.avro.UserCreated"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate simple spring kafka client with external native avro payload type`() {
- val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
- val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreatedAvro.kt").readText()
-
- assertTrue(consumerContent.contains("import com.example.external.avro.UserCreatedAvro"))
- assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.external.avro.UserCreatedAvro"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate simple spring kafka client with native protobuf payload type`() {
- val yaml = File("src/test/resources/generator/asyncapi_native_protobuf_spring_kafka_client.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
- val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreated.kt").readText()
-
- assertTrue(consumerContent.contains("import com.example.protobuf.UserCreated"))
- assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.protobuf.UserCreated"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-
- @Test
- fun `should generate simple spring kafka client with external native protobuf payload type`() {
- val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
- val modelPackage = "dev.banking.test.userservice.v1.model"
- val clientPackage = "dev.banking.test.userservice.v1.client"
-
- generateElement(
- yaml = yaml,
- modelPackage = modelPackage,
- clientPackage = clientPackage,
- generateModels = false,
- generateSpringKafkaClient = true,
- springKafkaClientType = SpringKafkaClientType.SIMPLE,
- )
-
- val outputDir = File("target/generated-sources/asyncapi")
- val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
- val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreatedProtobuf.kt").readText()
-
- assertTrue(consumerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf"))
- assertTrue(consumerContent.contains("ConsumerRecord"))
- assertTrue(producerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf"))
- assertTrue(producerContent.contains("KafkaTemplate"))
- }
-}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaTest.kt
index 92b3cb48..092486ea 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GenerateKotlinSpringKafkaTest.kt
@@ -3,12 +3,12 @@ package dev.banking.asyncapi.generator.core.generator.kotlin.kafka
import dev.banking.asyncapi.generator.core.generator.AbstractKotlinGeneratorClass
import org.junit.jupiter.api.Test
import java.io.File
+import kotlin.test.assertFalse
import kotlin.test.assertTrue
class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
-
@Test
- fun `should generate full spring kafka ecosystem`() {
+ fun `should generate spring kafka client`() {
val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -22,115 +22,118 @@ class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
)
val outputDir = File("target/generated-sources/asyncapi")
- val modelPath = "dev/banking/test/userservice/v1/model"
val clientPath = "dev/banking/test/userservice/v1/client"
+ val producerDir = outputDir.resolve("$clientPath/producer")
+ val consumerDir = outputDir.resolve("$clientPath/consumer")
+
+ val producerFile = producerDir.resolve("UserEventsProducerUserSignedUp.kt")
+ assertTrue(producerFile.exists(), "Producer should be generated")
+ val producerContent = producerFile.readText()
+ assertTrue(producerContent.contains("class UserEventsProducerUserSignedUp"))
+ assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("sendUserSignedUp"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
+ assertTrue(producerContent.contains("return kafkaTemplate.send"))
+ assertTrue(!producerContent.contains("@Component"), "Producer should not be annotated")
+
+ val consumerFile = consumerDir.resolve("UserEventsConsumer.kt")
+ assertTrue(consumerFile.exists(), "Consumer should be generated")
+ val consumerContent = consumerFile.readText()
+ assertTrue(consumerContent.contains("interface UserEventsConsumer"))
+ assertTrue(consumerContent.contains("fun onUserSignedUp"))
+ assertTrue(consumerContent.contains("ConsumerRecord"))
+ assertFalse(consumerContent.contains("{ }"), "Consumer methods should be abstract")
+ assertTrue(!consumerContent.contains("@KafkaListener"), "Consumer should not be annotated")
+ }
- val modelDir = outputDir.resolve(modelPath)
- assertTrue(modelDir.resolve("UserSignedUpPayload.kt").exists(), "UserSignedUpPayload model missing")
- assertTrue(modelDir.resolve("UserLoggedInPayload.kt").exists(), "UserLoggedInPayload model missing")
+ @Test
+ fun `should generate header classes for spring kafka client`() {
+ val yaml = File("src/test/resources/generator/asyncapi_message_headers.yaml")
+ val modelPackage = "dev.banking.test.userservice.v1.model"
+ val clientPackage = "dev.banking.test.userservice.v1.client"
- val clientDir = outputDir.resolve(clientPath)
- val autoconfigDir = clientDir.resolve("config")
- val handlerDir = clientDir.resolve("handler")
- val listenerDir = clientDir.resolve("listener")
- val producerDir = clientDir.resolve("producer")
- assertTrue(
- listenerDir.resolve("TopicUserEventsListenerUserSignedUp.kt").exists(),
- "UserSignedUp Listener missing",
- )
- assertTrue(handlerDir.resolve("TopicUserEventsHandlerUserSignedUp.kt").exists(), "UserSignedUp Handler missing")
- assertTrue(
- listenerDir.resolve("TopicUserEventsListenerUserLoggedIn.kt").exists(),
- "UserLoggedIn Listener missing",
- )
- assertTrue(handlerDir.resolve("TopicUserEventsHandlerUserLoggedIn.kt").exists(), "UserLoggedIn Handler missing")
- assertTrue(producerDir.resolve("TopicUserEventsProducerUserSignedUp.kt").exists(), "UserSignedUp Producer missing")
- assertTrue(producerDir.resolve("TopicUserEventsProducerUserLoggedIn.kt").exists(), "UserLoggedIn Producer missing")
- val userSignedUpListenerContent = listenerDir.resolve("TopicUserEventsListenerUserSignedUp.kt").readText()
- assertTrue(
- userSignedUpListenerContent.contains("ConsumerRecord"),
- "Listener should be typed to UserSignedUp",
- )
- assertTrue(
- userSignedUpListenerContent.contains("import $modelPackage.UserSignedUp"),
- "Missing correct Model Import",
- )
- assertTrue(
- userSignedUpListenerContent.contains("import org.springframework.boot.autoconfigure.condition.ConditionalOnBean"),
- "Missing ConditionalOnBean import",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@ConditionalOnBean(TopicUserEventsHandlerUserSignedUp::class)"),
- "Missing @ConditionalOnBean annotation",
- )
- val userProducerContent = producerDir.resolve("TopicUserEventsProducerUserSignedUp.kt").readText()
- assertTrue(
- userProducerContent.contains("@ConditionalOnProperty(name = [\"kafka.topics.userEvents\"])"),
- "Missing @ConditionalOnProperty annotation",
- )
- assertTrue(
- userProducerContent.contains("@Value(\"\\\${kafka.topics.userEvents}\")"),
- "Producer should read topic from kafka.topics.userEvents",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@ConditionalOnProperty(name = [\"kafka.topics.userEvents\"])"),
- "Listener should be conditional on topic property",
- )
- assertTrue(
- userSignedUpListenerContent.contains("@KafkaListener(topics = [\"\\\${kafka.topics.userEvents}\"]"),
- "Listener should read topic from kafka.topics.userEvents",
+ generateElement(
+ yaml = yaml,
+ modelPackage = modelPackage,
+ clientPackage = clientPackage,
+ generateModels = true,
+ generateSpringKafkaClient = true,
)
- val autoConfigContent = autoconfigDir.resolve("AsyncApiKafkaAutoConfiguration.kt").readText()
- assertTrue(autoConfigContent.contains("@ComponentScan"), "Auto-configuration should include ComponentScan")
- assertTrue(
- autoConfigContent.contains("basePackages = [\"$clientPackage\"]"),
- "Auto-configuration should scan the client package",
- )
+ val outputDir = File("target/generated-sources/asyncapi")
+ val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
+ val headerDir = outputDir.resolve("dev/banking/test/userservice/v1/client/header")
+ assertTrue(headerDir.exists(), "Spring Kafka client should generate header classes")
- val importsFile =
- File("target/generated-resources/asyncapi/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports")
- assertTrue(importsFile.exists(), "Auto-configuration imports file should be generated")
- val importsContent = importsFile.readText()
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ assertTrue(consumerContent.contains("import dev.banking.test.userservice.v1.client.header.TopicUserEventsHeadersUserSignup"))
+ assertTrue(
+ consumerContent.contains(
+ "fun onUserSignup(record: ConsumerRecord, " +
+ "headers: TopicUserEventsHeadersUserSignup)",
+ ),
+ )
+ assertFalse(consumerContent.contains("{ }"), "Consumer methods should be abstract")
+
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserSignup.kt").readText()
+ assertTrue(producerContent.contains("import dev.banking.test.userservice.v1.client.header.TopicUserEventsHeadersUserSignup"))
+ assertTrue(producerContent.contains("import java.util.concurrent.CompletableFuture"))
+ assertTrue(producerContent.contains("import org.springframework.kafka.support.SendResult"))
+ assertTrue(producerContent.contains("fun sendUserSignup("))
+ assertTrue(producerContent.contains("message: UserSignupPayload"))
+ assertTrue(producerContent.contains("headers: TopicUserEventsHeadersUserSignup"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
assertTrue(
- importsContent.contains("$clientPackage.config.AsyncApiKafkaAutoConfiguration"),
- "Auto-configuration imports should include generated config",
+ producerContent.contains(
+ "headers.correlationId?.let { record.headers().add(\"correlationId\", " +
+ "it.toString().toByteArray(Charsets.UTF_8)) }",
+ ),
)
}
@Test
- fun `should apply custom topic property prefix`() {
- val yaml = File("src/test/resources/generator/asyncapi_spring_kafka_client_example.yaml")
+ fun `should not reference typed headers when Kafka header generation is disabled`() {
+ val yaml = File("src/test/resources/generator/asyncapi_message_headers.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
- val outputDir = File("target/generated-sources/asyncapi")
+ val outputDir = File("target/generated-sources/asyncapi-kotlin-spring-kafka-no-headers")
+ val resourceOutputDirectory = File("target/generated-resources/asyncapi-kotlin-spring-kafka-no-headers")
+ outputDir.deleteRecursively()
+ resourceOutputDirectory.deleteRecursively()
generateElement(
yaml = yaml,
+ codegenOutputDirectory = outputDir,
+ resourceOutputDirectory = resourceOutputDirectory,
modelPackage = modelPackage,
clientPackage = clientPackage,
generateModels = true,
generateSpringKafkaClient = true,
- codegenOutputDirectory = outputDir,
- kafkaTopicsPropertyPrefix = "my.property",
+ generateKafkaHeaders = false,
)
+
val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val producerDir = clientDir.resolve("producer")
- val listenerDir = clientDir.resolve("listener")
- val producerContent = producerDir.resolve("TopicUserEventsProducerUserSignedUp.kt").readText()
- val listenerContent = listenerDir.resolve("TopicUserEventsListenerUserSignedUp.kt").readText()
- assertTrue(
- producerContent.contains("@Value(\"\\\${my.property.userEvents}\")"),
- "Producer should use custom topic property key",
- )
- assertTrue(
- listenerContent.contains("@KafkaListener(topics = [\"\\\${my.property.userEvents}\"]"),
- "Listener should use custom topic property key",
- )
+ val headerDir = clientDir.resolve("header")
+ assertFalse(headerDir.exists(), "Header classes should not be generated when Kafka headers are disabled")
+
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ assertFalse(consumerContent.contains(".client.header."))
+ assertFalse(consumerContent.contains("TopicUserEventsHeadersUserSignup"))
+ assertTrue(consumerContent.contains("fun onUserSignup(record: ConsumerRecord)"))
+ assertFalse(consumerContent.contains("{ }"), "Consumer methods should be abstract")
+
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserSignup.kt").readText()
+ assertFalse(producerContent.contains(".client.header."))
+ assertFalse(producerContent.contains("TopicUserEventsHeadersUserSignup"))
+ assertFalse(producerContent.contains("record.headers().add"))
+ assertFalse(producerContent.contains("headers:"))
+ assertTrue(producerContent.contains("fun sendUserSignup("))
+ assertTrue(producerContent.contains("message: UserSignupPayload"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate full spring kafka client with native avro payload type`() {
+ fun `should generate spring kafka client with native avro payload type`() {
val yaml = File("src/test/resources/generator/asyncapi_native_avro_spring_kafka_client.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -145,17 +148,18 @@ class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
val outputDir = File("target/generated-sources/asyncapi")
val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreated.kt").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreated.kt").readText()
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreated.kt").readText()
- assertTrue(listenerContent.contains("import com.example.avro.UserCreated"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
+ assertTrue(consumerContent.contains("import com.example.avro.UserCreated"))
+ assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.avro.UserCreated"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate full spring kafka client with external native avro payload type`() {
+ fun `should generate spring kafka client with external native avro payload type`() {
val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -170,17 +174,18 @@ class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
val outputDir = File("target/generated-sources/asyncapi")
val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreatedAvro.kt").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreatedAvro.kt").readText()
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreatedAvro.kt").readText()
- assertTrue(listenerContent.contains("import com.example.external.avro.UserCreatedAvro"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
+ assertTrue(consumerContent.contains("import com.example.external.avro.UserCreatedAvro"))
+ assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.external.avro.UserCreatedAvro"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate full spring kafka client with native protobuf payload type`() {
+ fun `should generate spring kafka client with native protobuf payload type`() {
val yaml = File("src/test/resources/generator/asyncapi_native_protobuf_spring_kafka_client.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -195,17 +200,18 @@ class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
val outputDir = File("target/generated-sources/asyncapi")
val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreated.kt").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreated.kt").readText()
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreated.kt").readText()
- assertTrue(listenerContent.contains("import com.example.protobuf.UserCreated"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
+ assertTrue(consumerContent.contains("import com.example.protobuf.UserCreated"))
+ assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.protobuf.UserCreated"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
@Test
- fun `should generate full spring kafka client with external native protobuf payload type`() {
+ fun `should generate spring kafka client with external native protobuf payload type`() {
val yaml = File("src/test/resources/generator/native-assets/asyncapi_external_native_schema_assets.yaml")
val modelPackage = "dev.banking.test.userservice.v1.model"
val clientPackage = "dev.banking.test.userservice.v1.client"
@@ -220,12 +226,13 @@ class GenerateKotlinSpringKafkaTest : AbstractKotlinGeneratorClass() {
val outputDir = File("target/generated-sources/asyncapi")
val clientDir = outputDir.resolve("dev/banking/test/userservice/v1/client")
- val listenerContent = clientDir.resolve("listener/TopicUserEventsListenerUserCreatedProtobuf.kt").readText()
- val producerContent = clientDir.resolve("producer/TopicUserEventsProducerUserCreatedProtobuf.kt").readText()
+ val consumerContent = clientDir.resolve("consumer/UserEventsConsumer.kt").readText()
+ val producerContent = clientDir.resolve("producer/UserEventsProducerUserCreatedProtobuf.kt").readText()
- assertTrue(listenerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf"))
- assertTrue(listenerContent.contains("ConsumerRecord"))
+ assertTrue(consumerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf"))
+ assertTrue(consumerContent.contains("ConsumerRecord"))
assertTrue(producerContent.contains("import com.example.external.protobuf.UserCreatedProtobuf"))
assertTrue(producerContent.contains("KafkaTemplate"))
+ assertTrue(producerContent.contains("CompletableFuture>"))
}
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GeneratePrimitivePayloadTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GeneratePrimitivePayloadTest.kt
index 38e7e760..efe73add 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GeneratePrimitivePayloadTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/kotlin/kafka/GeneratePrimitivePayloadTest.kt
@@ -35,12 +35,10 @@ class GeneratePrimitivePayloadTest : AbstractKotlinGeneratorClass() {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val handlerFile =
- outputDir.resolve(packageName.replace('.', '/') + "/handler/TopicSimpleTopicHandlerSimpleStringMessage.kt")
+ outputDir.resolve(packageName.replace('.', '/') + "/consumer/SimpleTopicConsumer.kt")
assertTrue(handlerFile.exists())
val content = handlerFile.readText()
@@ -53,7 +51,7 @@ class GeneratePrimitivePayloadTest : AbstractKotlinGeneratorClass() {
packageName.replace(
'.',
'/'
- ) + "/producer/TopicSimpleTopicProducerSimpleStringMessage.kt"
+ ) + "/producer/SimpleTopicProducerSimpleStringMessage.kt"
)
assertTrue(producerFile.exists(), "Producer should be generated")
val producerContent = producerFile.readText()
@@ -61,6 +59,10 @@ class GeneratePrimitivePayloadTest : AbstractKotlinGeneratorClass() {
producerContent.contains("KafkaTemplate"),
"Producer should use typed KafkaTemplate for single payload",
)
+ assertTrue(
+ producerContent.contains("CompletableFuture>"),
+ "Producer should return the Spring Kafka send result future",
+ )
}
@Test
@@ -94,14 +96,12 @@ class GeneratePrimitivePayloadTest : AbstractKotlinGeneratorClass() {
outputDir,
packageName,
packageName,
- "kafka.topics",
- File("target/generated-resources/asyncapi"),
)
generator.generate(listOf(channel))
val producerFileA =
- outputDir.resolve(packageName.replace('.', '/') + "/producer/TopicMultiTopicProducerStringMessage.kt")
+ outputDir.resolve(packageName.replace('.', '/') + "/producer/MultiTopicProducerStringMessage.kt")
val producerFileB =
- outputDir.resolve(packageName.replace('.', '/') + "/producer/TopicMultiTopicProducerIntMessage.kt")
+ outputDir.resolve(packageName.replace('.', '/') + "/producer/MultiTopicProducerIntMessage.kt")
assertTrue(producerFileA.exists(), "StringMessage producer should be generated")
assertTrue(producerFileB.exists(), "IntMessage producer should be generated")
val producerContentA = producerFileA.readText()
@@ -110,9 +110,17 @@ class GeneratePrimitivePayloadTest : AbstractKotlinGeneratorClass() {
producerContentA.contains("KafkaTemplate"),
"StringMessage producer should use typed KafkaTemplate",
)
+ assertTrue(
+ producerContentA.contains("CompletableFuture>"),
+ "StringMessage producer should return the Spring Kafka send result future",
+ )
assertTrue(
producerContentB.contains("KafkaTemplate"),
"IntMessage producer should use typed KafkaTemplate",
)
+ assertTrue(
+ producerContentB.contains("CompletableFuture>"),
+ "IntMessage producer should return the Spring Kafka send result future",
+ )
}
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlannerTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlannerTest.kt
index ddebe07b..d392e847 100644
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlannerTest.kt
+++ b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/GenerationPlannerTest.kt
@@ -11,7 +11,6 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
class GenerationPlannerTest {
private val planner = GenerationPlanner()
@@ -129,11 +128,11 @@ class GenerationPlannerTest {
}
@Test
- fun `plan includes header and full Spring Kafka client tasks for full client generation`() {
+ fun `plan includes header and Spring Kafka client tasks for Spring Kafka client generation`() {
val plan =
planner.plan(
generatorConfiguration(
- clients = listOf(springKafkaClientGeneration(clientType = SpringKafkaClientType.FULL)),
+ clients = listOf(kafkaClientGeneration()),
),
)
@@ -143,60 +142,82 @@ class GenerationPlannerTest {
language = GeneratorName.KOTLIN,
packageName = "com.example.client.header",
),
- springKafkaClientTask(clientType = SpringKafkaClientType.FULL),
+ springKafkaClientTask(),
),
plan.tasks,
)
}
@Test
- fun `plan accepts explicit full Spring Kafka client type`() {
+ fun `plan can disable Kafka header generation`() {
val plan =
planner.plan(
generatorConfiguration(
- clients = listOf(springKafkaClientGeneration(clientType = SpringKafkaClientType.FULL)),
+ clients =
+ listOf(
+ kafkaClientGeneration(
+ headers = ClientGeneration.Headers(enabled = false),
+ ),
+ ),
),
)
assertEquals(
listOf(
- GenerationTask.HeaderModelArtifacts(
- language = GeneratorName.KOTLIN,
- packageName = "com.example.client.header",
+ springKafkaClientTask(
+ generateHeaders = false,
),
- springKafkaClientTask(clientType = SpringKafkaClientType.FULL),
),
plan.tasks,
)
}
@Test
- fun `plan excludes header model task for simple Spring Kafka client generation`() {
+ fun `plan includes Spring Kafka producer and consumer options on Spring Kafka client task`() {
val plan =
planner.plan(
generatorConfiguration(
- clients = listOf(springKafkaClientGeneration(clientType = SpringKafkaClientType.SIMPLE)),
+ clients =
+ listOf(
+ kafkaClientGeneration(
+ springKafka =
+ ClientGeneration.SpringKafka(
+ producer = ClientGeneration.Producer(enabled = false),
+ consumer = ClientGeneration.Consumer(enabled = true),
+ ),
+ ),
+ ),
),
)
assertEquals(
listOf(
- springKafkaClientTask(clientType = SpringKafkaClientType.SIMPLE),
+ GenerationTask.HeaderModelArtifacts(
+ language = GeneratorName.KOTLIN,
+ packageName = "com.example.client.header",
+ ),
+ springKafkaClientTask(
+ generateProducers = false,
+ generateConsumers = true,
+ ),
),
plan.tasks,
)
}
@Test
- fun `plan includes custom topic prefix on Spring Kafka client task`() {
+ fun `plan skips Spring Kafka client task when producer and consumer are disabled`() {
val plan =
planner.plan(
generatorConfiguration(
clients =
listOf(
- springKafkaClientGeneration(
- clientType = SpringKafkaClientType.FULL,
- topicPropertyPrefix = "custom.topics",
+ kafkaClientGeneration(
+ springKafka =
+ ClientGeneration.SpringKafka(
+ producer = ClientGeneration.Producer(enabled = false),
+ consumer = ClientGeneration.Consumer(enabled = false),
+ ),
),
),
),
@@ -208,15 +229,33 @@ class GenerationPlannerTest {
language = GeneratorName.KOTLIN,
packageName = "com.example.client.header",
),
- springKafkaClientTask(
- clientType = SpringKafkaClientType.FULL,
- topicPropertyPrefix = "custom.topics",
- ),
),
plan.tasks,
)
}
+ @Test
+ fun `plan skips Kafka tasks when every Kafka capability is disabled`() {
+ val plan =
+ planner.plan(
+ generatorConfiguration(
+ clients =
+ listOf(
+ kafkaClientGeneration(
+ headers = ClientGeneration.Headers(enabled = false),
+ springKafka =
+ ClientGeneration.SpringKafka(
+ producer = ClientGeneration.Producer(enabled = false),
+ consumer = ClientGeneration.Consumer(enabled = false),
+ ),
+ ),
+ ),
+ ),
+ )
+
+ assertEquals(emptyList(), plan.tasks)
+ }
+
@Test
fun `plan uses selected language for language-specific tasks`() {
val plan =
@@ -226,7 +265,7 @@ class GenerationPlannerTest {
models = ModelGeneration.Enabled(packageName = "com.example.model"),
clients =
listOf(
- springKafkaClientGeneration(clientType = SpringKafkaClientType.FULL),
+ kafkaClientGeneration(),
ClientGeneration.QuarkusKafka(
packageName = "com.example.client",
modelPackageName = "com.example.model",
@@ -247,7 +286,6 @@ class GenerationPlannerTest {
),
springKafkaClientTask(
language = GeneratorName.JAVA,
- clientType = SpringKafkaClientType.FULL,
),
GenerationTask.QuarkusKafkaClient(
language = GeneratorName.JAVA,
@@ -257,26 +295,6 @@ class GenerationPlannerTest {
)
}
- @Test
- fun `plan rejects Spring Kafka client generation with blank topic prefix`() {
- val exception =
- assertFailsWith {
- planner.plan(
- generatorConfiguration(
- clients =
- listOf(
- springKafkaClientGeneration(
- clientType = SpringKafkaClientType.FULL,
- topicPropertyPrefix = "",
- ),
- ),
- ),
- )
- }
-
- assertEquals("topicPropertyPrefix cannot be empty", exception.message)
- }
-
private fun generatorConfiguration(
language: GeneratorName = GeneratorName.KOTLIN,
models: ModelGeneration = ModelGeneration.Disabled,
@@ -295,31 +313,33 @@ class GenerationPlannerTest {
clients = clients,
)
- private fun springKafkaClientGeneration(
- clientType: SpringKafkaClientType,
+ private fun kafkaClientGeneration(
clientPackage: String = "com.example.client",
modelPackage: String = "com.example.model",
- topicPropertyPrefix: String = "kafka.topics",
- ): ClientGeneration.SpringKafka =
- ClientGeneration.SpringKafka(
+ headers: ClientGeneration.Headers = ClientGeneration.Headers(),
+ springKafka: ClientGeneration.SpringKafka? = ClientGeneration.SpringKafka(),
+ ): ClientGeneration.Kafka =
+ ClientGeneration.Kafka(
packageName = clientPackage,
modelPackageName = modelPackage,
- clientType = clientType,
- topicPropertyPrefix = topicPropertyPrefix,
+ headers = headers,
+ springKafka = springKafka,
)
private fun springKafkaClientTask(
language: GeneratorName = GeneratorName.KOTLIN,
- clientType: SpringKafkaClientType,
clientPackage: String = "com.example.client",
modelPackage: String = "com.example.model",
- topicPropertyPrefix: String = "kafka.topics",
+ generateHeaders: Boolean = true,
+ generateProducers: Boolean = true,
+ generateConsumers: Boolean = true,
): GenerationTask.SpringKafkaClient =
GenerationTask.SpringKafkaClient(
language = language,
- clientType = clientType,
clientPackage = clientPackage,
modelPackage = modelPackage,
- topicPropertyPrefix = topicPropertyPrefix,
+ generateHeaders = generateHeaders,
+ generateProducers = generateProducers,
+ generateConsumers = generateConsumers,
)
}
diff --git a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientTypeTest.kt b/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientTypeTest.kt
deleted file mode 100644
index 7b46333a..00000000
--- a/asyncapi-generator-core/src/test/kotlin/dev/banking/asyncapi/generator/core/generator/plan/SpringKafkaClientTypeTest.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package dev.banking.asyncapi.generator.core.generator.plan
-
-import org.junit.jupiter.api.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-class SpringKafkaClientTypeTest {
- @Test
- fun `fromConfigurationValue defaults to simple when value is not configured`() {
- assertEquals(
- SpringKafkaClientType.SIMPLE,
- SpringKafkaClientType.fromConfigurationValue(
- value = null,
- path = "clients.springKafka.mode",
- ),
- )
- }
-
- @Test
- fun `fromConfigurationValue parses supported configuration values`() {
- assertEquals(
- SpringKafkaClientType.FULL,
- SpringKafkaClientType.fromConfigurationValue(
- value = "full",
- path = "clients.springKafka.mode",
- ),
- )
- assertEquals(
- SpringKafkaClientType.SIMPLE,
- SpringKafkaClientType.fromConfigurationValue(
- value = "simple",
- path = "clients.springKafka.mode",
- ),
- )
- }
-
- @Test
- fun `fromConfigurationValue rejects unsupported configuration values`() {
- val exception =
- assertFailsWith {
- SpringKafkaClientType.fromConfigurationValue(
- value = "basic",
- path = "clients.springKafka.mode",
- )
- }
-
- assertEquals(
- "Invalid clients.springKafka.mode 'basic'. Supported values: full, simple",
- exception.message,
- )
- }
-}
diff --git a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPlugin.kt b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPlugin.kt
index bf8b3af7..ebe2b71a 100644
--- a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPlugin.kt
+++ b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPlugin.kt
@@ -40,11 +40,13 @@ class AsyncApiPlugin : Plugin {
nativeProtobufEnabled.set(extension.schemas.nativeProtobuf.enabled)
nativeProtobufGenerateJavaMessageTypes.set(extension.schemas.nativeProtobuf.generateJavaMessageTypes)
- springKafkaEnabled.set(extension.clients.springKafka.enabled)
- springKafkaPackageName.set(extension.clients.springKafka.packageName)
- springKafkaModelPackageName.set(extension.clients.springKafka.modelPackageName)
- springKafkaMode.set(extension.clients.springKafka.mode)
- springKafkaTopicPropertyPrefix.set(extension.clients.springKafka.topicPropertyPrefix)
+ kafkaEnabled.set(extension.clients.kafka.enabled)
+ kafkaPackageName.set(extension.clients.kafka.packageName)
+ kafkaModelPackageName.set(extension.clients.kafka.modelPackageName)
+ kafkaHeadersEnabled.set(extension.clients.kafka.headers.enabled)
+ kafkaSpringKafkaEnabled.set(extension.clients.kafka.springKafka.enabled)
+ kafkaSpringKafkaProducerEnabled.set(extension.clients.kafka.springKafka.producer.enabled)
+ kafkaSpringKafkaConsumerEnabled.set(extension.clients.kafka.springKafka.consumer.enabled)
quarkusKafkaEnabled.set(extension.clients.quarkusKafka.enabled)
quarkusKafkaPackageName.set(extension.clients.quarkusKafka.packageName)
diff --git a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/extensions/AsyncApiExtension.kt b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/extensions/AsyncApiExtension.kt
index 81c3b05c..6e551bee 100644
--- a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/extensions/AsyncApiExtension.kt
+++ b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/extensions/AsyncApiExtension.kt
@@ -118,13 +118,13 @@ abstract class AsyncApiNativeProtobufExtension @Inject constructor(objects: Obje
* - `AsyncApiPluginTest`
*/
abstract class AsyncApiClientsExtension @Inject constructor(objects: ObjectFactory) {
- val springKafka: AsyncApiSpringKafkaExtension =
- objects.newInstance(AsyncApiSpringKafkaExtension::class.java)
+ val kafka: AsyncApiKafkaExtension =
+ objects.newInstance(AsyncApiKafkaExtension::class.java)
val quarkusKafka: AsyncApiQuarkusKafkaExtension =
objects.newInstance(AsyncApiQuarkusKafkaExtension::class.java)
- fun springKafka(action: Action) {
- action.execute(springKafka)
+ fun kafka(action: Action) {
+ action.execute(kafka)
}
fun quarkusKafka(action: Action) {
@@ -133,17 +133,79 @@ abstract class AsyncApiClientsExtension @Inject constructor(objects: ObjectFacto
}
/**
- * Gradle Spring Kafka client configuration.
+ * Gradle Kafka client configuration.
*
* Expected behavior is covered by:
* - `AsyncApiPluginTest`
*/
-abstract class AsyncApiSpringKafkaExtension @Inject constructor(objects: ObjectFactory) {
+abstract class AsyncApiKafkaExtension @Inject constructor(objects: ObjectFactory) {
val enabled: Property = objects.property(Boolean::class.javaObjectType)
val packageName: Property = objects.property(String::class.java)
val modelPackageName: Property = objects.property(String::class.java)
- val mode: Property = objects.property(String::class.java)
- val topicPropertyPrefix: Property = objects.property(String::class.java)
+ val headers: AsyncApiKafkaHeadersExtension =
+ objects.newInstance(AsyncApiKafkaHeadersExtension::class.java)
+ val springKafka: AsyncApiKafkaSpringKafkaExtension =
+ objects.newInstance(AsyncApiKafkaSpringKafkaExtension::class.java)
+
+ fun headers(action: Action) {
+ action.execute(headers)
+ }
+
+ fun springKafka(action: Action) {
+ action.execute(springKafka)
+ }
+}
+
+/**
+ * Gradle Kafka header generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiPluginTest`
+ */
+abstract class AsyncApiKafkaHeadersExtension @Inject constructor(objects: ObjectFactory) {
+ val enabled: Property = objects.property(Boolean::class.javaObjectType)
+}
+
+/**
+ * Gradle Spring Kafka client generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiPluginTest`
+ */
+abstract class AsyncApiKafkaSpringKafkaExtension @Inject constructor(objects: ObjectFactory) {
+ val enabled: Property = objects.property(Boolean::class.javaObjectType)
+ val producer: AsyncApiKafkaProducerExtension =
+ objects.newInstance(AsyncApiKafkaProducerExtension::class.java)
+ val consumer: AsyncApiKafkaConsumerExtension =
+ objects.newInstance(AsyncApiKafkaConsumerExtension::class.java)
+
+ fun producer(action: Action) {
+ action.execute(producer)
+ }
+
+ fun consumer(action: Action) {
+ action.execute(consumer)
+ }
+}
+
+/**
+ * Gradle Spring Kafka producer generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiPluginTest`
+ */
+abstract class AsyncApiKafkaProducerExtension @Inject constructor(objects: ObjectFactory) {
+ val enabled: Property = objects.property(Boolean::class.javaObjectType)
+}
+
+/**
+ * Gradle Spring Kafka consumer generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiPluginTest`
+ */
+abstract class AsyncApiKafkaConsumerExtension @Inject constructor(objects: ObjectFactory) {
+ val enabled: Property = objects.property(Boolean::class.javaObjectType)
}
/**
diff --git a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/tasks/GenerateAsyncApiTask.kt b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/tasks/GenerateAsyncApiTask.kt
index ad12cb23..a449946f 100644
--- a/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/tasks/GenerateAsyncApiTask.kt
+++ b/asyncapi-generator-gradle-plugin/src/main/kotlin/dev/banking/asyncapi/generator/gradle/plugin/tasks/GenerateAsyncApiTask.kt
@@ -76,23 +76,31 @@ abstract class GenerateAsyncApiTask : DefaultTask() {
@get:Input
@get:Optional
- abstract val springKafkaEnabled: Property
+ abstract val kafkaEnabled: Property
@get:Input
@get:Optional
- abstract val springKafkaPackageName: Property
+ abstract val kafkaPackageName: Property
@get:Input
@get:Optional
- abstract val springKafkaModelPackageName: Property
+ abstract val kafkaModelPackageName: Property
@get:Input
@get:Optional
- abstract val springKafkaMode: Property
+ abstract val kafkaHeadersEnabled: Property
@get:Input
@get:Optional
- abstract val springKafkaTopicPropertyPrefix: Property
+ abstract val kafkaSpringKafkaEnabled: Property
+
+ @get:Input
+ @get:Optional
+ abstract val kafkaSpringKafkaProducerEnabled: Property
+
+ @get:Input
+ @get:Optional
+ abstract val kafkaSpringKafkaConsumerEnabled: Property
@get:Input
@get:Optional
@@ -175,11 +183,13 @@ abstract class GenerateAsyncApiTask : DefaultTask() {
),
clients =
clientRequest(
- springKafkaEnabled = springKafkaEnabled.orNull,
- springKafkaPackageName = springKafkaPackageName.orNull,
- springKafkaModelPackageName = springKafkaModelPackageName.orNull,
- springKafkaMode = springKafkaMode.orNull,
- springKafkaTopicPropertyPrefix = springKafkaTopicPropertyPrefix.orNull,
+ kafkaEnabled = kafkaEnabled.orNull,
+ kafkaPackageName = kafkaPackageName.orNull,
+ kafkaModelPackageName = kafkaModelPackageName.orNull,
+ kafkaHeadersEnabled = kafkaHeadersEnabled.orNull,
+ kafkaSpringKafkaEnabled = kafkaSpringKafkaEnabled.orNull,
+ kafkaSpringKafkaProducerEnabled = kafkaSpringKafkaProducerEnabled.orNull,
+ kafkaSpringKafkaConsumerEnabled = kafkaSpringKafkaConsumerEnabled.orNull,
quarkusKafkaEnabled = quarkusKafkaEnabled.orNull,
quarkusKafkaPackageName = quarkusKafkaPackageName.orNull,
quarkusKafkaModelPackageName = quarkusKafkaModelPackageName.orNull,
@@ -232,23 +242,27 @@ abstract class GenerateAsyncApiTask : DefaultTask() {
)
private fun clientRequest(
- springKafkaEnabled: Boolean?,
- springKafkaPackageName: String?,
- springKafkaModelPackageName: String?,
- springKafkaMode: String?,
- springKafkaTopicPropertyPrefix: String?,
+ kafkaEnabled: Boolean?,
+ kafkaPackageName: String?,
+ kafkaModelPackageName: String?,
+ kafkaHeadersEnabled: Boolean?,
+ kafkaSpringKafkaEnabled: Boolean?,
+ kafkaSpringKafkaProducerEnabled: Boolean?,
+ kafkaSpringKafkaConsumerEnabled: Boolean?,
quarkusKafkaEnabled: Boolean?,
quarkusKafkaPackageName: String?,
quarkusKafkaModelPackageName: String?,
): GeneratorConfigurationRequest.Clients =
GeneratorConfigurationRequest.Clients(
- springKafka =
- springKafkaRequest(
- enabled = springKafkaEnabled,
- packageName = springKafkaPackageName,
- modelPackageName = springKafkaModelPackageName,
- mode = springKafkaMode,
- topicPropertyPrefix = springKafkaTopicPropertyPrefix,
+ kafka =
+ kafkaRequest(
+ enabled = kafkaEnabled,
+ packageName = kafkaPackageName,
+ modelPackageName = kafkaModelPackageName,
+ headersEnabled = kafkaHeadersEnabled,
+ springKafkaEnabled = kafkaSpringKafkaEnabled,
+ springKafkaProducerEnabled = kafkaSpringKafkaProducerEnabled,
+ springKafkaConsumerEnabled = kafkaSpringKafkaConsumerEnabled,
),
quarkusKafka =
GeneratorConfigurationRequest.quarkusKafka(
@@ -258,18 +272,25 @@ abstract class GenerateAsyncApiTask : DefaultTask() {
),
)
- private fun springKafkaRequest(
+ private fun kafkaRequest(
enabled: Boolean?,
packageName: String?,
modelPackageName: String?,
- mode: String?,
- topicPropertyPrefix: String?,
- ): GeneratorConfigurationRequest.SpringKafka? =
- GeneratorConfigurationRequest.springKafka(
+ headersEnabled: Boolean?,
+ springKafkaEnabled: Boolean?,
+ springKafkaProducerEnabled: Boolean?,
+ springKafkaConsumerEnabled: Boolean?,
+ ): GeneratorConfigurationRequest.Kafka? =
+ GeneratorConfigurationRequest.kafka(
enabled = enabled,
packageName = packageName,
modelPackageName = modelPackageName,
- mode = mode,
- topicPropertyPrefix = topicPropertyPrefix,
+ headers = GeneratorConfigurationRequest.kafkaHeaders(enabled = headersEnabled),
+ springKafka =
+ GeneratorConfigurationRequest.kafkaSpringKafka(
+ enabled = springKafkaEnabled,
+ producer = GeneratorConfigurationRequest.kafkaProducer(enabled = springKafkaProducerEnabled),
+ consumer = GeneratorConfigurationRequest.kafkaConsumer(enabled = springKafkaConsumerEnabled),
+ ),
)
}
diff --git a/asyncapi-generator-gradle-plugin/src/test/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPluginTest.kt b/asyncapi-generator-gradle-plugin/src/test/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPluginTest.kt
index 44bb1aed..7add6b0c 100644
--- a/asyncapi-generator-gradle-plugin/src/test/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPluginTest.kt
+++ b/asyncapi-generator-gradle-plugin/src/test/kotlin/dev/banking/asyncapi/generator/gradle/plugin/AsyncApiPluginTest.kt
@@ -110,38 +110,10 @@ class AsyncApiPluginTest {
packageName.set("com.example.model")
}
clients {
- springKafka {
- enabled.set(true)
- }
- }
- }""")
- val result = GradleTestHelper.runGradleAndFail(projectDir, "generateAsyncApi")
- assertEquals(TaskOutcome.FAILED, result.task(":generateAsyncApi")?.outcome)
- assertTrue(
- result.output.contains(
- "clients.springKafka.packageName is required when clients.springKafka is configured",
- ),
- )
- }
-
- @Test
- fun `should fail if spring kafka mode is invalid`() {
- val projectDir = Files.createTempDirectory("gradleTest").toFile()
- val yamlUrl = GradleTestHelper.resourceFile("asyncapi_kafka_complex.yaml")
- File(yamlUrl.toURI()).copyTo(File(projectDir, "api.yaml"))
- GradleTestHelper.writeBuildScript(projectDir, """
- plugins { id("dev.banking.asyncapi.generator") }
- asyncapiGenerate {
- inputFile.set(file("api.yaml"))
- codegenOutputDirectory.set(layout.buildDirectory.dir("generated/asyncapi"))
- generatorName.set("kotlin")
- models {
- packageName.set("com.example.model")
- }
- clients {
- springKafka {
- packageName.set("com.example.client")
- mode.set("basic")
+ kafka {
+ springKafka {
+ enabled.set(true)
+ }
}
}
}""")
@@ -149,7 +121,7 @@ class AsyncApiPluginTest {
assertEquals(TaskOutcome.FAILED, result.task(":generateAsyncApi")?.outcome)
assertTrue(
result.output.contains(
- "Invalid clients.springKafka.mode 'basic'. Supported values: full, simple",
+ "clients.kafka.packageName is required when clients.kafka is configured",
),
)
}
@@ -260,8 +232,11 @@ class AsyncApiPluginTest {
packageName.set("com.example.kafka.model")
}
clients {
- springKafka {
+ kafka {
packageName.set("com.example.kafka.client")
+ springKafka {
+ enabled.set(true)
+ }
}
}
}"""
@@ -291,9 +266,12 @@ class AsyncApiPluginTest {
codegenOutputDirectory.set(layout.buildDirectory.dir("generated/asyncapi"))
generatorName.set("kotlin")
clients {
- springKafka {
+ kafka {
packageName.set("com.example.kafka.client")
modelPackageName.set("com.example.kafka.model")
+ springKafka {
+ enabled.set(true)
+ }
}
}
}"""
@@ -327,8 +305,11 @@ class AsyncApiPluginTest {
packageName.set("com.example.kafka.model")
}
clients {
- springKafka {
+ kafka {
packageName.set("com.example.kafka.client")
+ springKafka {
+ enabled.set(true)
+ }
}
}
}"""
diff --git a/asyncapi-generator-maven-plugin/src/main/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenGeneratorConfiguration.kt b/asyncapi-generator-maven-plugin/src/main/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenGeneratorConfiguration.kt
index 02d39ffa..3c648b8d 100644
--- a/asyncapi-generator-maven-plugin/src/main/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenGeneratorConfiguration.kt
+++ b/asyncapi-generator-maven-plugin/src/main/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenGeneratorConfiguration.kt
@@ -100,39 +100,97 @@ class MavenNativeProtobufConfiguration {
* - `AsyncApiGeneratorMojoTest`
*/
class MavenClientGenerationConfiguration {
- var springKafka: MavenSpringKafkaConfiguration? = null
+ var kafka: MavenKafkaConfiguration? = null
var quarkusKafka: MavenQuarkusKafkaConfiguration? = null
fun toRequest(): GeneratorConfigurationRequest.Clients =
GeneratorConfigurationRequest.Clients(
- springKafka = springKafka?.toRequest(),
+ kafka = kafka?.toRequest(),
quarkusKafka = quarkusKafka?.toRequest(),
)
}
/**
- * Maven Spring Kafka client configuration.
+ * Maven Kafka client configuration.
*
* Expected behavior is covered by:
* - `AsyncApiGeneratorMojoTest`
*/
-class MavenSpringKafkaConfiguration {
+class MavenKafkaConfiguration {
var enabled: Boolean? = null
var packageName: String? = null
var modelPackageName: String? = null
- var mode: String? = null
- var topicPropertyPrefix: String? = null
+ var headers: MavenKafkaHeadersConfiguration? = null
+ var springKafka: MavenKafkaSpringKafkaConfiguration? = null
- fun toRequest(): GeneratorConfigurationRequest.SpringKafka? =
- GeneratorConfigurationRequest.springKafka(
+ fun toRequest(): GeneratorConfigurationRequest.Kafka? =
+ GeneratorConfigurationRequest.kafka(
enabled = enabled,
packageName = packageName,
modelPackageName = modelPackageName,
- mode = mode,
- topicPropertyPrefix = topicPropertyPrefix,
+ headers = headers?.toRequest(),
+ springKafka = springKafka?.toRequest(),
+ )
+}
+
+/**
+ * Maven Kafka header generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiGeneratorMojoTest`
+ */
+class MavenKafkaHeadersConfiguration {
+ var enabled: Boolean? = null
+
+ fun toRequest(): GeneratorConfigurationRequest.KafkaHeaders? =
+ GeneratorConfigurationRequest.kafkaHeaders(enabled = enabled)
+}
+
+/**
+ * Maven Spring Kafka client generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiGeneratorMojoTest`
+ */
+class MavenKafkaSpringKafkaConfiguration {
+ var enabled: Boolean? = null
+ var producer: MavenKafkaProducerConfiguration? = null
+ var consumer: MavenKafkaConsumerConfiguration? = null
+
+ fun toRequest(): GeneratorConfigurationRequest.KafkaSpringKafka? =
+ GeneratorConfigurationRequest.kafkaSpringKafka(
+ enabled = enabled ?: true,
+ producer = producer?.toRequest(),
+ consumer = consumer?.toRequest(),
)
}
+/**
+ * Maven Spring Kafka producer generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiGeneratorMojoTest`
+ */
+class MavenKafkaProducerConfiguration {
+ var enabled: Boolean? = null
+
+ fun toRequest(): GeneratorConfigurationRequest.KafkaProducer? =
+ GeneratorConfigurationRequest.kafkaProducer(enabled = enabled)
+}
+
+/**
+ * Maven Spring Kafka consumer generation configuration.
+ *
+ * Expected behavior is covered by:
+ * - `AsyncApiGeneratorMojoTest`
+ */
+class MavenKafkaConsumerConfiguration {
+ var enabled: Boolean? = null
+
+ fun toRequest(): GeneratorConfigurationRequest.KafkaConsumer? =
+ GeneratorConfigurationRequest.kafkaConsumer(enabled = enabled)
+}
+
/**
* Maven Quarkus Kafka client configuration.
*
diff --git a/asyncapi-generator-maven-plugin/src/test/it/config-merge/pom.xml b/asyncapi-generator-maven-plugin/src/test/it/config-merge/pom.xml
index 6207fb42..1e2f0ff9 100644
--- a/asyncapi-generator-maven-plugin/src/test/it/config-merge/pom.xml
+++ b/asyncapi-generator-maven-plugin/src/test/it/config-merge/pom.xml
@@ -24,9 +24,12 @@
com.example.a.model
-
+
com.example.a.client
-
+
+ true
+
+
@@ -47,9 +50,9 @@
com.example.b.model
-
+
false
-
+
@@ -62,10 +65,12 @@
kotlin
-
+
true
- full
-
+
+ true
+
+
diff --git a/asyncapi-generator-maven-plugin/src/test/it/per-execution-only/pom.xml b/asyncapi-generator-maven-plugin/src/test/it/per-execution-only/pom.xml
index a4437c1b..25d7b48e 100644
--- a/asyncapi-generator-maven-plugin/src/test/it/per-execution-only/pom.xml
+++ b/asyncapi-generator-maven-plugin/src/test/it/per-execution-only/pom.xml
@@ -24,9 +24,12 @@
com.example.a.model
-
+
com.example.a.client
-
+
+ true
+
+
diff --git a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/pom.xml b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/pom.xml
similarity index 71%
rename from asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/pom.xml
rename to asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/pom.xml
index 8a49f964..5d1efa05 100644
--- a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/pom.xml
+++ b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/pom.xml
@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
dev.banking.asyncapi.generator.it
- spring-kafka-simple
+ spring-kafka-contract
1.0-SNAPSHOT
@@ -13,7 +13,7 @@
1.0-SNAPSHOT
- spring-kafka-simple
+ spring-kafka-contract
compile
generate
@@ -22,13 +22,15 @@
${project.basedir}/src/main/resources/asyncapi.yaml
kotlin
- com.example.simple.model
+ com.example.contract.model
-
- com.example.simple.client
- simple
-
+
+ com.example.contract.client
+
+ true
+
+
diff --git a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/src/main/resources/asyncapi.yaml b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/src/main/resources/asyncapi.yaml
similarity index 90%
rename from asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/src/main/resources/asyncapi.yaml
rename to asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/src/main/resources/asyncapi.yaml
index 572342f1..d925e6a6 100644
--- a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/src/main/resources/asyncapi.yaml
+++ b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/src/main/resources/asyncapi.yaml
@@ -1,6 +1,6 @@
asyncapi: 3.0.0
info:
- title: Spring Kafka Simple IT
+ title: Spring Kafka Contract IT
version: 1.0.0
channels:
userEvents:
diff --git a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/verify.groovy b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/verify.groovy
similarity index 58%
rename from asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/verify.groovy
rename to asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/verify.groovy
index 9872783c..627d696b 100644
--- a/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-simple/verify.groovy
+++ b/asyncapi-generator-maven-plugin/src/test/it/spring-kafka-contract/verify.groovy
@@ -1,15 +1,17 @@
-def producer = new File(basedir, "target/generated-sources/asyncapi/com/example/simple/client/producer/UserEventsProducerUserSignedUp.kt")
+def producer = new File(basedir, "target/generated-sources/asyncapi/com/example/contract/client/producer/UserEventsProducerUserSignedUp.kt")
assert producer.exists() : "Expected UserEventsProducerUserSignedUp.kt to be generated"
def producerContent = producer.text
assert producerContent.contains("class UserEventsProducerUserSignedUp") : "Expected producer class name"
assert producerContent.contains("KafkaTemplate") : "Expected typed KafkaTemplate"
-assert !producerContent.contains("@Component") : "Simple producer should not be annotated"
+assert producerContent.contains("CompletableFuture>") : "Expected producer send future return type"
+assert !producerContent.contains("@Component") : "Contract producer should not be annotated"
-def consumer = new File(basedir, "target/generated-sources/asyncapi/com/example/simple/client/consumer/UserEventsConsumer.kt")
+def consumer = new File(basedir, "target/generated-sources/asyncapi/com/example/contract/client/consumer/UserEventsConsumer.kt")
assert consumer.exists() : "Expected UserEventsConsumer.kt to be generated"
def consumerContent = consumer.text
assert consumerContent.contains("interface UserEventsConsumer") : "Expected consumer interface"
assert consumerContent.contains("fun onUserSignedUp") : "Expected onUserSignedUp method"
-assert !consumerContent.contains("@KafkaListener") : "Simple consumer should not be annotated"
+assert !consumerContent.contains("{ }") : "Expected abstract consumer method"
+assert !consumerContent.contains("@KafkaListener") : "Contract consumer should not be annotated"
diff --git a/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/AsyncApiGeneratorMojoTest.kt b/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/AsyncApiGeneratorMojoTest.kt
index 0ca4835c..0ba41994 100644
--- a/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/AsyncApiGeneratorMojoTest.kt
+++ b/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/AsyncApiGeneratorMojoTest.kt
@@ -6,6 +6,7 @@ import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.codegenOutput
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.generatorName
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.inputPath
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.javaSourceOutputDirectory
+import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.kafka
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.nativeAvro
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.nativeProtobuf
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.outputPath
@@ -15,7 +16,6 @@ import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.outputFile
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.project
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.resourceOutputDirectory
import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.schemas
-import dev.banking.asyncapi.generator.maven.plugin.MavenTestHelper.springKafka
import org.apache.maven.plugin.MojoExecutionException
import org.apache.maven.project.MavenProject
import org.junit.jupiter.api.Assertions.assertEquals
@@ -50,7 +50,7 @@ class AsyncApiGeneratorMojoTest {
codegenOutputDirectory(outputPath("target/generated-sources/asyncapi"))
resourceOutputDirectory(outputPath("target/generated-resources/asyncapi"))
models(models(packageName = "com.example.kafka.model"))
- clients(clients(springKafka = springKafka(packageName = "com.example.kafka.client")))
+ clients(clients(kafka = kafka(packageName = "com.example.kafka.client")))
generatorName("kotlin")
}.execute()
val clientDir = File("target/generated-sources/asyncapi/com/example/kafka/client")
@@ -65,7 +65,7 @@ class AsyncApiGeneratorMojoTest {
codegenOutputDirectory(outputPath("target/generated-sources/asyncapi"))
resourceOutputDirectory(outputPath("target/generated-resources/asyncapi"))
models(models(packageName = "com.example.kafka.model"))
- clients(clients(springKafka = springKafka(packageName = "com.example.kafka.client")))
+ clients(clients(kafka = kafka(packageName = "com.example.kafka.client")))
generatorName("java")
}.execute()
val clientDir = File("target/generated-sources/asyncapi/com/example/kafka/client")
@@ -98,8 +98,8 @@ class AsyncApiGeneratorMojoTest {
resourceOutputDirectory(outputPath("target/generated-resources/asyncapi-client-only"))
clients(
clients(
- springKafka =
- springKafka(
+ kafka =
+ kafka(
packageName = "com.example.kafka.client",
modelPackageName = "com.example.kafka.model",
),
@@ -270,22 +270,6 @@ class AsyncApiGeneratorMojoTest {
)
}
- @Test
- fun `should fail when spring kafka mode is invalid`() {
- val mojo = AsyncApiGeneratorMojo().apply {
- project(MavenProject())
- inputFile(inputPath("asyncapi_valid_content_kotlin.yaml"))
- codegenOutputDirectory(outputPath("target/should-fail-client-mode"))
- resourceOutputDirectory(outputPath("target/generated-resources/asyncapi"))
- models(models(packageName = "com.fail"))
- clients(clients(springKafka = springKafka(packageName = "com.fail.client", mode = "basic")))
- generatorName("kotlin")
- }
- assertThrows {
- mojo.execute()
- }
- }
-
@Test
fun `should fail when java model type is invalid`() {
val mojo = AsyncApiGeneratorMojo().apply {
diff --git a/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenTestHelper.kt b/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenTestHelper.kt
index 73df1297..14116665 100644
--- a/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenTestHelper.kt
+++ b/asyncapi-generator-maven-plugin/src/test/kotlin/dev/banking/asyncapi/generator/maven/plugin/MavenTestHelper.kt
@@ -111,26 +111,52 @@ object MavenTestHelper {
}
fun clients(
- springKafka: MavenSpringKafkaConfiguration? = null,
+ kafka: MavenKafkaConfiguration? = null,
quarkusKafka: MavenQuarkusKafkaConfiguration? = null,
): MavenClientGenerationConfiguration =
MavenClientGenerationConfiguration().apply {
- this.springKafka = springKafka
+ this.kafka = kafka
this.quarkusKafka = quarkusKafka
}
- fun springKafka(
+ fun kafka(
packageName: String? = null,
modelPackageName: String? = null,
- mode: String? = null,
- topicPropertyPrefix: String? = null,
enabled: Boolean? = null,
- ): MavenSpringKafkaConfiguration =
- MavenSpringKafkaConfiguration().apply {
+ headers: MavenKafkaHeadersConfiguration? = null,
+ springKafka: MavenKafkaSpringKafkaConfiguration? = springKafka(),
+ ): MavenKafkaConfiguration =
+ MavenKafkaConfiguration().apply {
this.packageName = packageName
this.modelPackageName = modelPackageName
- this.mode = mode
- this.topicPropertyPrefix = topicPropertyPrefix
+ this.enabled = enabled
+ this.headers = headers
+ this.springKafka = springKafka
+ }
+
+ fun kafkaHeaders(enabled: Boolean? = null): MavenKafkaHeadersConfiguration =
+ MavenKafkaHeadersConfiguration().apply {
+ this.enabled = enabled
+ }
+
+ fun springKafka(
+ enabled: Boolean? = null,
+ producer: MavenKafkaProducerConfiguration? = null,
+ consumer: MavenKafkaConsumerConfiguration? = null,
+ ): MavenKafkaSpringKafkaConfiguration =
+ MavenKafkaSpringKafkaConfiguration().apply {
+ this.enabled = enabled
+ this.producer = producer
+ this.consumer = consumer
+ }
+
+ fun kafkaProducer(enabled: Boolean? = null): MavenKafkaProducerConfiguration =
+ MavenKafkaProducerConfiguration().apply {
+ this.enabled = enabled
+ }
+
+ fun kafkaConsumer(enabled: Boolean? = null): MavenKafkaConsumerConfiguration =
+ MavenKafkaConsumerConfiguration().apply {
this.enabled = enabled
}