From 44dfa042be49d424cf2cdc441bc5c944fbc6deda Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 28 Nov 2024 09:32:44 +1100 Subject: [PATCH] kotlin --- backend/controller/pubsub/integration_test.go | 5 +- .../pubsub/testdata/kotlin/publisher/ftl.toml | 2 + .../pubsub/testdata/kotlin/publisher/pom.xml | 14 ++++ .../ftl/java/test/publisher/PubSubEvent.kt | 6 ++ .../ftl/java/test/publisher/Publisher.kt | 49 +++++++++++++ .../testdata/kotlin/subscriber/ftl.toml | 2 + .../pubsub/testdata/kotlin/subscriber/pom.xml | 14 ++++ .../ftl/java/test/subscriber/Subscriber.kt | 73 +++++++++++++++++++ docs/content/docs/reference/pubsub.md | 2 +- jvm-runtime/plugin/common/java_plugin_test.go | 8 +- jvm-runtime/plugin/common/jvmcommon.go | 8 +- 11 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 backend/controller/pubsub/testdata/kotlin/publisher/ftl.toml create mode 100644 backend/controller/pubsub/testdata/kotlin/publisher/pom.xml create mode 100644 backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/PubSubEvent.kt create mode 100644 backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/Publisher.kt create mode 100644 backend/controller/pubsub/testdata/kotlin/subscriber/ftl.toml create mode 100644 backend/controller/pubsub/testdata/kotlin/subscriber/pom.xml create mode 100644 backend/controller/pubsub/testdata/kotlin/subscriber/src/main/kotlin/xyz/block/ftl/java/test/subscriber/Subscriber.kt diff --git a/backend/controller/pubsub/integration_test.go b/backend/controller/pubsub/integration_test.go index d1740c4a4a..52e951c390 100644 --- a/backend/controller/pubsub/integration_test.go +++ b/backend/controller/pubsub/integration_test.go @@ -8,17 +8,18 @@ import ( "testing" "time" + "github.com/alecthomas/assert/v2" + "github.com/TBD54566975/ftl/backend/controller/async" in "github.com/TBD54566975/ftl/internal/integration" "github.com/TBD54566975/ftl/internal/schema" - "github.com/alecthomas/assert/v2" ) func TestPubSub(t *testing.T) { calls := 20 events := calls * 10 in.Run(t, - in.WithLanguages("java", "go"), + in.WithLanguages("java", "go", "kotlin"), in.CopyModule("publisher"), in.CopyModule("subscriber"), in.Deploy("publisher"), diff --git a/backend/controller/pubsub/testdata/kotlin/publisher/ftl.toml b/backend/controller/pubsub/testdata/kotlin/publisher/ftl.toml new file mode 100644 index 0000000000..e08f334217 --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/publisher/ftl.toml @@ -0,0 +1,2 @@ +module = "publisher" +language = "java" diff --git a/backend/controller/pubsub/testdata/kotlin/publisher/pom.xml b/backend/controller/pubsub/testdata/kotlin/publisher/pom.xml new file mode 100644 index 0000000000..d9e4135f7e --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/publisher/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + xyz.block.ftl.examples + publisher + 1.0-SNAPSHOT + + + xyz.block.ftl + ftl-build-parent-kotlin + 1.0-SNAPSHOT + + + diff --git a/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/PubSubEvent.kt b/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/PubSubEvent.kt new file mode 100644 index 0000000000..0eddc9843a --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/PubSubEvent.kt @@ -0,0 +1,6 @@ +package xyz.block.ftl.java.test.publisher + +import java.time.ZonedDateTime +class PubSubEvent(var time: ZonedDateTime) { + +} diff --git a/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/Publisher.kt b/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/Publisher.kt new file mode 100644 index 0000000000..0549c44787 --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/publisher/src/main/kotlin/xyz/block/ftl/java/test/publisher/Publisher.kt @@ -0,0 +1,49 @@ +package xyz.block.ftl.java.test.publisher + +import io.quarkus.logging.Log +import xyz.block.ftl.* +import java.time.ZonedDateTime + +class Publisher { + @Export + @Topic("testTopic") + interface TestTopic : WriteableTopic + + @Topic("localTopic") + interface LocalTopic : WriteableTopic + + @Export + @Topic("topic2") + interface Topic2 : WriteableTopic + + @Verb + @Throws(Exception::class) + fun publishTen(testTopic: LocalTopic) { + for (i in 0..9) { + val t = ZonedDateTime.now() + Log.infof("Publishing %s", t) + testTopic.publish(PubSubEvent(t)) + } + } + + @Verb + @Throws(Exception::class) + fun publishOne(testTopic: TestTopic) { + val t = ZonedDateTime.now() + Log.infof("Publishing %s", t) + testTopic.publish(PubSubEvent(t)) + } + + @Verb + @Throws(Exception::class) + fun publishOneToTopic2(topic2: Topic2) { + val t = ZonedDateTime.now() + Log.infof("Publishing %s", t) + topic2.publish(PubSubEvent(t)) + } + + @Subscription(topic = LocalTopic::class, from = FromOffset.LATEST) + fun local(testTopic: TestTopic, event: PubSubEvent) { + testTopic.publish(event) + } +} diff --git a/backend/controller/pubsub/testdata/kotlin/subscriber/ftl.toml b/backend/controller/pubsub/testdata/kotlin/subscriber/ftl.toml new file mode 100644 index 0000000000..9d8b272e91 --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/subscriber/ftl.toml @@ -0,0 +1,2 @@ +module = "subscriber" +language = "java" diff --git a/backend/controller/pubsub/testdata/kotlin/subscriber/pom.xml b/backend/controller/pubsub/testdata/kotlin/subscriber/pom.xml new file mode 100644 index 0000000000..3d63cc1fd7 --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/subscriber/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + xyz.block.ftl.examples + subscriber + 1.0-SNAPSHOT + + + xyz.block.ftl + ftl-build-parent-kotlin + 1.0-SNAPSHOT + + + diff --git a/backend/controller/pubsub/testdata/kotlin/subscriber/src/main/kotlin/xyz/block/ftl/java/test/subscriber/Subscriber.kt b/backend/controller/pubsub/testdata/kotlin/subscriber/src/main/kotlin/xyz/block/ftl/java/test/subscriber/Subscriber.kt new file mode 100644 index 0000000000..09aa03fe31 --- /dev/null +++ b/backend/controller/pubsub/testdata/kotlin/subscriber/src/main/kotlin/xyz/block/ftl/java/test/subscriber/Subscriber.kt @@ -0,0 +1,73 @@ +package xyz.block.ftl.java.test.subscriber + +import ftl.builtin.CatchRequest +import ftl.publisher.PubSubEvent +import ftl.publisher.TestTopicTopic +import ftl.publisher.Topic2Topic +import io.quarkus.logging.Log +import xyz.block.ftl.* +import java.util.concurrent.atomic.AtomicInteger + +class Subscriber { + @Subscription(topic = TestTopicTopic::class, from = FromOffset.BEGINNING) + @Throws( + Exception::class + ) + fun consume(event: PubSubEvent) { + Log.infof("Subscriber is consuming %s", event.time) + } + + @Subscription(topic = Topic2Topic::class, from = FromOffset.BEGINNING) + @Retry(count = 2, minBackoff = "1s", maxBackoff = "1s", catchVerb = "catch") + fun consumeButFailAndRetry(event: PubSubEvent) { + throw RuntimeException("always error: event " + event.time) + } + + @Subscription(topic = Topic2Topic::class, from = FromOffset.BEGINNING) + @Retry(count = 1, minBackoff = "1s", maxBackoff = "1s", catchVerb = "catchAny") + fun consumeButFailAndCatchAny(event: PubSubEvent) { + throw RuntimeException("always error: event " + event.time) + } + + @Verb + @VerbName("catch") + fun catchVerb(req: CatchRequest) { + if (!req.error.contains("always error: event")) { + throw RuntimeException("unexpected error: " + req.error) + } + if (catchCount.incrementAndGet() == 1) { + throw RuntimeException("catching error") + } + } + + @Verb + fun catchAny(req: CatchRequest) { + require("subscriber" == req.verb.module) { String.format("unexpected verb module: %s", req.verb.module) } + require("consumeButFailAndCatchAny" == req.verb.name) { + String.format( + "unexpected verb name: %s", + req.verb.name + ) + } + require("publisher.PubSubEvent" == req.requestType) { + String.format( + "unexpected request type: %s", + req.requestType + ) + } + require(req.request is Map<*, *>) { + String.format( + "expected request to be a Map: %s", + req.request.javaClass.name + ) + } + val request = req.request as Map<*, *> + val time = request["time"] + requireNotNull(time) { "expected request to have a time key" } + require(time is String) { "expected request to have a time value of type string" } + } + + companion object { + private val catchCount = AtomicInteger() + } +} diff --git a/docs/content/docs/reference/pubsub.md b/docs/content/docs/reference/pubsub.md index 0ce945a8ca..413c042252 100644 --- a/docs/content/docs/reference/pubsub.md +++ b/docs/content/docs/reference/pubsub.md @@ -63,7 +63,7 @@ fun publishInvoice(request: InvoiceRequest, topic: InvoiceTopic) { To subscribe to a topic use the `@Subscription` annotation, referencing the topic class and providing a method to consume the event: ```kotlin -@Subscription(topic = InvoiceTopic, from = FromOffset.LATEST) +@Subscription(topic = InvoiceTopic::class, from = FromOffset.LATEST) fun consumeInvoice(event: Invoice) { // ... } diff --git a/jvm-runtime/plugin/common/java_plugin_test.go b/jvm-runtime/plugin/common/java_plugin_test.go index 913af94743..bd37b175b1 100644 --- a/jvm-runtime/plugin/common/java_plugin_test.go +++ b/jvm-runtime/plugin/common/java_plugin_test.go @@ -30,8 +30,8 @@ func TestJavaConfigDefaults(t *testing.T) { language: "kotlin", dir: "testdata/kotlin/echo", expected: moduleconfig.CustomDefaults{ - Build: optional.Some("mvn -B package"), - DevModeBuild: optional.Some("mvn quarkus:dev"), + Build: optional.Some("mvn -B clean package"), + DevModeBuild: optional.Some("mvn clean quarkus:dev"), DeployDir: "target", GeneratedSchemaDir: optional.Some("src/main/ftl-module-schema"), LanguageConfig: map[string]any{ @@ -49,8 +49,8 @@ func TestJavaConfigDefaults(t *testing.T) { language: "kotlin", dir: "testdata/kotlin/external", expected: moduleconfig.CustomDefaults{ - Build: optional.Some("mvn -B package"), - DevModeBuild: optional.Some("mvn quarkus:dev"), + Build: optional.Some("mvn -B clean package"), + DevModeBuild: optional.Some("mvn clean quarkus:dev"), DeployDir: "target", GeneratedSchemaDir: optional.Some("src/main/ftl-module-schema"), LanguageConfig: map[string]any{ diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index 3e025f9bd9..e0057fc94b 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -581,13 +581,13 @@ func (s *Service) ModuleConfigDefaults(ctx context.Context, req *connect.Request buildGradleKts := filepath.Join(dir, "build.gradle.kts") if fileExists(pom) { defaults.LanguageConfig.Fields["build-tool"] = structpb.NewStringValue(JavaBuildToolMaven) - defaults.DevModeBuild = ptr("mvn quarkus:dev") - defaults.Build = ptr("mvn -B package") + defaults.DevModeBuild = ptr("mvn clean quarkus:dev") + defaults.Build = ptr("mvn -B clean package") defaults.DeployDir = "target" } else if fileExists(buildGradle) || fileExists(buildGradleKts) { defaults.LanguageConfig.Fields["build-tool"] = structpb.NewStringValue(JavaBuildToolGradle) - defaults.DevModeBuild = ptr("gradle quarkusDev") - defaults.Build = ptr("gradle build") + defaults.DevModeBuild = ptr("gradle clean quarkusDev") + defaults.Build = ptr("gradle clean build") defaults.DeployDir = "build" } else { return nil, fmt.Errorf("could not find JVM build file in %s", dir)