From 6ea439fbc877a71eff36ad30b1bdab70a8d78fa0 Mon Sep 17 00:00:00 2001 From: Dean Wette Date: Fri, 18 Aug 2023 12:15:59 -0500 Subject: [PATCH 1/4] Multi-language documentation for Injecting Kafka Producer Beans example --- .../docs/producer/inject/BookSenderTest.java | 20 ------------- .../guide/kafkaClient/kafkaClientScope.adoc | 15 +++------- .../kafka/docs/producer/inject/Book.groovy | 12 ++++++++ .../docs/producer/inject/BookSender.groovy | 29 +++++++++++++++++++ .../producer/inject/BookSenderTest.groovy | 22 ++++++++++++++ .../kafka/docs/producer/inject/Book.kt | 6 ++++ .../kafka/docs/producer/inject/BookSender.kt | 23 +++++++++++++++ .../docs/producer/inject/BookSenderTest.kt | 19 ++++++++++++ .../kafka/docs/producer/inject/Book.java | 6 ++++ .../docs/producer/inject/BookSender.java | 9 +++--- .../docs/producer/inject/BookSenderTest.java | 22 ++++++++++++++ 11 files changed, 147 insertions(+), 36 deletions(-) delete mode 100644 kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSenderTest.java create mode 100644 test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy create mode 100644 test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSender.groovy create mode 100644 test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy create mode 100644 test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/Book.kt create mode 100644 test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSender.kt create mode 100644 test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt create mode 100644 test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/Book.java rename {kafka/src/test/groovy/io/micronaut/configuration => test-suite/src/test/java/io/micronaut}/kafka/docs/producer/inject/BookSender.java (72%) create mode 100644 test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java diff --git a/kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSenderTest.java b/kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSenderTest.java deleted file mode 100644 index 56778b7e6..000000000 --- a/kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSenderTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.micronaut.configuration.kafka.docs.producer.inject; - -import io.micronaut.configuration.kafka.docs.consumer.batch.Book; -import io.micronaut.context.ApplicationContext; -import org.junit.Test; - -public class BookSenderTest { - - // tag::test[] - @Test - public void testBookSender() { - try (ApplicationContext ctx = ApplicationContext.run()) { // <1> - BookSender bookSender = ctx.getBean(BookSender.class); // <2> - Book book = new Book(); - book.setTitle("The Stand"); - bookSender.send("Stephen King", book); - } - } - // end::test[] -} diff --git a/src/main/docs/guide/kafkaClient/kafkaClientScope.adoc b/src/main/docs/guide/kafkaClient/kafkaClientScope.adoc index 50cfe5fce..f29b1bad3 100644 --- a/src/main/docs/guide/kafkaClient/kafkaClientScope.adoc +++ b/src/main/docs/guide/kafkaClient/kafkaClientScope.adoc @@ -3,12 +3,8 @@ If you need maximum flexibility and don't want to use the ann:configuration.kafk Consider the following example: .Using a KafkaProducer directly -[source,java] ----- -include::{testskafka}/producer/inject/BookSender.java[tags=imports, indent=0] -include::{testskafka}/producer/inject/BookSender.java[tags=clazz, indent=0] ----- +snippet::io.micronaut.kafka.docs.producer.inject.BookSender[tags="imports,clazz"] <1> The `Producer` is dependency injected into the constructor. If not specified in configuration, the key and value serializer are inferred from the generic type arguments. <2> The `Producer` is used to send records @@ -17,14 +13,11 @@ Note that there is no need to call the `close()` method to shut down the `KafkaP The previous example can be tested in JUnit with the following test: - .Using a KafkaProducer directly -[source,java] ----- -include::{testskafka}/producer/inject/BookSenderTest.java[tags=test, indent=0] ----- + +snippet::io.micronaut.kafka.docs.producer.inject.BookSenderTest[tags=test, indent=0] <1> A Kafka docker container is used <2> The `BookSender` is retrieved from the api:context.ApplicationContext[] and a `ProducerRecord` sent -By using the link:{kafkaapi}/org/apache/kafka/clients/producer/KafkaProducer.html[KafkaProducer] API directly you open up even more options if you require transactions (exactly-once delivery) or want control over when records are flushed etc. \ No newline at end of file +By using the link:{kafkaapi}/org/apache/kafka/clients/producer/KafkaProducer.html[KafkaProducer] API directly you open up even more options if you require transactions (exactly-once delivery) or want control over when records are flushed etc. diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy new file mode 100644 index 000000000..5a5fed300 --- /dev/null +++ b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy @@ -0,0 +1,12 @@ +package io.micronaut.kafka.docs.producer.inject + +import io.micronaut.serde.annotation.Serdeable + +@Serdeable +class Book { + String title + + Book(String title) { + this.title = title + } +} diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSender.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSender.groovy new file mode 100644 index 000000000..40280b753 --- /dev/null +++ b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSender.groovy @@ -0,0 +1,29 @@ +package io.micronaut.kafka.docs.producer.inject; + +// tag::imports[] +import io.micronaut.configuration.kafka.annotation.KafkaClient +import io.micronaut.context.annotation.Requires +import jakarta.inject.Singleton +import org.apache.kafka.clients.producer.Producer +import org.apache.kafka.clients.producer.ProducerRecord +import org.apache.kafka.clients.producer.RecordMetadata + +import java.util.concurrent.Future +// end::imports[] + +@Requires(property = 'spec.name', value = 'BookSenderTest') +// tag::clazz[] +@Singleton +class BookSender { + + private final Producer kafkaProducer + + BookSender(@KafkaClient('book-producer') Producer kafkaProducer) { // <1> + this.kafkaProducer = kafkaProducer + } + + Future send(String author, Book book) { + kafkaProducer.send(new ProducerRecord<>('books', author, book)) // <2> + } +} +// end::clazz[] diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy new file mode 100644 index 000000000..29307590f --- /dev/null +++ b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy @@ -0,0 +1,22 @@ +package io.micronaut.kafka.docs.producer.inject + +import io.micronaut.context.ApplicationContext +import spock.lang.Specification + +class BookSenderTest extends Specification { + + // tag::test[] + void "test Book Sender"() { + expect: + ApplicationContext ctx = ApplicationContext.run( // <1> + "kafka.enabled": true, "spec.name": "BookSenderTest" + ) + BookSender bookSender = ctx.getBean(BookSender) // <2> + Book book = new Book('The Stand') + bookSender.send('Stephen King', book) + + cleanup: + ctx.close() + } + // end::test[] +} diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/Book.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/Book.kt new file mode 100644 index 000000000..f611659e5 --- /dev/null +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/Book.kt @@ -0,0 +1,6 @@ +package io.micronaut.kafka.docs.producer.inject + +import io.micronaut.serde.annotation.Serdeable + +@Serdeable +data class Book(val title: String) diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSender.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSender.kt new file mode 100644 index 000000000..c7de00722 --- /dev/null +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSender.kt @@ -0,0 +1,23 @@ +package io.micronaut.kafka.docs.producer.inject + +// tag::imports[] +import io.micronaut.configuration.kafka.annotation.KafkaClient +import io.micronaut.context.annotation.Requires +import jakarta.inject.Singleton +import org.apache.kafka.clients.producer.Producer +import org.apache.kafka.clients.producer.ProducerRecord +import org.apache.kafka.clients.producer.RecordMetadata +import java.util.concurrent.Future +// end::imports[] + +// tag::clazz[] +@Requires(property = "spec.name", value = "BookSenderTest") +@Singleton +class BookSender( + @param:KafkaClient("book-producer") private val kafkaProducer: Producer) { // <1> + + fun send(author: String, book: Book): Future { + return kafkaProducer.send(ProducerRecord("books", author, book)) // <2> + } +} +// end::clazz[] diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt new file mode 100644 index 000000000..1bfc92ee4 --- /dev/null +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt @@ -0,0 +1,19 @@ +package io.micronaut.kafka.docs.producer.inject + +import io.micronaut.context.ApplicationContext +import org.junit.jupiter.api.Test +import java.util.Map + +internal class BookSenderTest { + + // tag::test[] + @Test + fun testBookSender() { + ApplicationContext.run(Map.of( // <1> + "kafka.enabled", "true", "spec.name", "BookSenderTest")).use { ctx -> + val bookSender = ctx.getBean(BookSender::class.java) // <2> + val book = Book("The Stand") + bookSender.send("Stephen King", book) + } + } // end::test[] +} diff --git a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/Book.java b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/Book.java new file mode 100644 index 000000000..c59c6cd13 --- /dev/null +++ b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/Book.java @@ -0,0 +1,6 @@ +package io.micronaut.kafka.docs.producer.inject; + +import io.micronaut.serde.annotation.Serdeable; + +@Serdeable +public record Book (String title) {} diff --git a/kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSender.java b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSender.java similarity index 72% rename from kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSender.java rename to test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSender.java index af4831a8d..06ffd6e96 100644 --- a/kafka/src/test/groovy/io/micronaut/configuration/kafka/docs/producer/inject/BookSender.java +++ b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSender.java @@ -1,8 +1,8 @@ -package io.micronaut.configuration.kafka.docs.producer.inject; +package io.micronaut.kafka.docs.producer.inject; // tag::imports[] import io.micronaut.configuration.kafka.annotation.KafkaClient; -import io.micronaut.configuration.kafka.docs.consumer.batch.Book; +import io.micronaut.context.annotation.Requires; import jakarta.inject.Singleton; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; @@ -11,20 +11,19 @@ import java.util.concurrent.Future; // end::imports[] +@Requires(property = "spec.name", value = "BookSenderTest") // tag::clazz[] @Singleton public class BookSender { private final Producer kafkaProducer; - public BookSender( - @KafkaClient("book-producer") Producer kafkaProducer) { // <1> + public BookSender(@KafkaClient("book-producer") Producer kafkaProducer) { // <1> this.kafkaProducer = kafkaProducer; } public Future send(String author, Book book) { return kafkaProducer.send(new ProducerRecord<>("books", author, book)); // <2> } - } // end::clazz[] diff --git a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java new file mode 100644 index 000000000..6b8d6438b --- /dev/null +++ b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java @@ -0,0 +1,22 @@ +package io.micronaut.kafka.docs.producer.inject; + +import io.micronaut.context.ApplicationContext; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +class BookSenderTest { + + // tag::test[] + @Test + void testBookSender() { + + ApplicationContext ctx = ApplicationContext.run( // <1> + Map.of("kafka.enabled", "true","spec.name", "BookSenderTest") + ); + BookSender bookSender = ctx.getBean(BookSender.class); // <2> + Book book = new Book("The Stand"); + bookSender.send("Stephen King", book); + } + // end::test[] +} From 905a3130d0279945ad1a4205c108d8c8f450a348 Mon Sep 17 00:00:00 2001 From: Dean Wette Date: Mon, 21 Aug 2023 09:40:38 -0500 Subject: [PATCH 2/4] use @Canonical --- .../io/micronaut/kafka/docs/producer/inject/Book.groovy | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy index 5a5fed300..6bbdbbb8c 100644 --- a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy +++ b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/Book.groovy @@ -1,12 +1,10 @@ package io.micronaut.kafka.docs.producer.inject +import groovy.transform.Canonical import io.micronaut.serde.annotation.Serdeable @Serdeable +@Canonical class Book { String title - - Book(String title) { - this.title = title - } } From 933ecaa8db6b5c0cada3c198c61f033cba570160 Mon Sep 17 00:00:00 2001 From: Dean Wette Date: Mon, 21 Aug 2023 10:31:19 -0500 Subject: [PATCH 3/4] Update test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java Co-authored-by: Tim Yates --- .../kafka/docs/producer/inject/BookSenderTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java index 6b8d6438b..12e527b42 100644 --- a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java +++ b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java @@ -11,12 +11,13 @@ class BookSenderTest { @Test void testBookSender() { - ApplicationContext ctx = ApplicationContext.run( // <1> - Map.of("kafka.enabled", "true","spec.name", "BookSenderTest") - ); - BookSender bookSender = ctx.getBean(BookSender.class); // <2> - Book book = new Book("The Stand"); - bookSender.send("Stephen King", book); + try (ApplicationContext ctx = ApplicationContext.run( // <1> + Map.of("kafka.enabled", "true", "spec.name", "BookSenderTest") + )) { + BookSender bookSender = ctx.getBean(BookSender.class); // <2> + Book book = new Book("The Stand"); + bookSender.send("Stephen King", book); + } } // end::test[] } From 356e7286e454c957af8ab58fc50d5c210244ef2c Mon Sep 17 00:00:00 2001 From: Dean Wette Date: Mon, 21 Aug 2023 11:43:57 -0500 Subject: [PATCH 4/4] add assertions to tests --- .../docs/producer/inject/BookSenderTest.groovy | 15 +++++++++++++-- .../kafka/docs/producer/inject/BookSenderTest.kt | 11 ++++++++--- .../docs/producer/inject/BookSenderTest.java | 11 ++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy index 29307590f..9541e8611 100644 --- a/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy +++ b/test-suite-groovy/src/test/groovy/io/micronaut/kafka/docs/producer/inject/BookSenderTest.groovy @@ -1,19 +1,30 @@ package io.micronaut.kafka.docs.producer.inject import io.micronaut.context.ApplicationContext +import org.apache.kafka.clients.producer.RecordMetadata import spock.lang.Specification +import java.util.concurrent.Future + class BookSenderTest extends Specification { // tag::test[] void "test Book Sender"() { - expect: + given: ApplicationContext ctx = ApplicationContext.run( // <1> - "kafka.enabled": true, "spec.name": "BookSenderTest" + 'kafka.enabled': true, 'spec.name': 'BookSenderTest' ) BookSender bookSender = ctx.getBean(BookSender) // <2> Book book = new Book('The Stand') + + when: bookSender.send('Stephen King', book) + Future stephenKing = bookSender.send('Stephen King', book); + def recordMetadata = stephenKing.get(); + + then: + noExceptionThrown() + recordMetadata.topic() == 'books' cleanup: ctx.close() diff --git a/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt index 1bfc92ee4..7b439c6a4 100644 --- a/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt +++ b/test-suite-kotlin/src/test/kotlin/io/micronaut/kafka/docs/producer/inject/BookSenderTest.kt @@ -1,19 +1,24 @@ package io.micronaut.kafka.docs.producer.inject import io.micronaut.context.ApplicationContext +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import java.util.Map internal class BookSenderTest { // tag::test[] @Test fun testBookSender() { - ApplicationContext.run(Map.of( // <1> - "kafka.enabled", "true", "spec.name", "BookSenderTest")).use { ctx -> + ApplicationContext.run(mutableMapOf( // <1> + "kafka.enabled" to "true", "spec.name" to "BookSenderTest")).use { ctx -> val bookSender = ctx.getBean(BookSender::class.java) // <2> val book = Book("The Stand") bookSender.send("Stephen King", book) + val stephenKing = bookSender.send("Stephen King", book) + Assertions.assertDoesNotThrow { + val recordMetadata = stephenKing.get() + Assertions.assertEquals("books", recordMetadata.topic()) + } } } // end::test[] } diff --git a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java index 12e527b42..e0c7d4e13 100644 --- a/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java +++ b/test-suite/src/test/java/io/micronaut/kafka/docs/producer/inject/BookSenderTest.java @@ -1,9 +1,14 @@ package io.micronaut.kafka.docs.producer.inject; import io.micronaut.context.ApplicationContext; +import org.apache.kafka.clients.producer.RecordMetadata; import org.junit.jupiter.api.Test; import java.util.Map; +import java.util.concurrent.Future; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; class BookSenderTest { @@ -16,7 +21,11 @@ void testBookSender() { )) { BookSender bookSender = ctx.getBean(BookSender.class); // <2> Book book = new Book("The Stand"); - bookSender.send("Stephen King", book); + Future stephenKing = bookSender.send("Stephen King", book); + assertDoesNotThrow(() -> { + RecordMetadata recordMetadata = stephenKing.get(); + assertEquals("books", recordMetadata.topic()); + }); } } // end::test[]