Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
java: [ 17, 21 ]
java: [ 17, 21, 25 ]
steps:
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java }}
Expand Down
5 changes: 4 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ allprojects {
filters =
arrayOf(
com.github.jk1.license.filter.LicenseBundleNormalizer(
"$rootDir/config/license-normalizer-bundle.json", true))
"$rootDir/config/license-normalizer-bundle.json",
true,
)
)
}
}

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ repositories {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.10")
implementation("org.jetbrains.kotlin:kotlin-serialization:2.2.10")
implementation("com.diffplug.spotless:spotless-plugin-gradle:7.2.1")
implementation("com.diffplug.spotless:spotless-plugin-gradle:8.2.0")
}
1 change: 1 addition & 0 deletions bytebuddy-proxy-support/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {

testImplementation(libs.junit.jupiter)
testImplementation(libs.assertj)
testRuntimeOnly(libs.junit.platform.launcher)
}

tasks.withType<Javadoc> { isFailOnError = false }
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ suspend fun <Req, Res> Client.callSuspend(request: Request<Req, Res>): Response<
*/
suspend fun <Req, Res> Request<Req, Res>.send(
client: Client,
delay: Duration? = null
delay: Duration? = null,
): SendResponse<Res> {
return client.sendSuspend(this, delay)
}
Expand All @@ -67,7 +67,7 @@ suspend fun <Req, Res> Request<Req, Res>.send(
*/
suspend fun <Req, Res> Client.sendSuspend(
request: Request<Req, Res>,
delay: Duration? = null
delay: Duration? = null,
): SendResponse<Res> {
return this.sendAsync(request, delay?.toJavaDuration()).await()
}
Expand All @@ -81,15 +81,15 @@ suspend fun <Req, Res> Client.sendSuspend(
*/
suspend fun <Req, Res> WorkflowRequest<Req, Res>.submit(
client: Client,
delay: Duration? = null
delay: Duration? = null,
): SendResponse<Res> {
return client.submitSuspend(this, delay)
}

/** Submit a workflow, optionally providing an execution delay to wait for. */
suspend fun <Req, Res> Client.submitSuspend(
request: WorkflowRequest<Req, Res>,
delay: Duration? = null
delay: Duration? = null,
): SendResponse<Res> {
return this.submitAsync(request, delay?.toJavaDuration()).await()
}
Expand All @@ -104,7 +104,7 @@ suspend fun <Req, Res> Client.submitSuspend(
suspend fun <T : Any> Client.AwakeableHandle.resolveSuspend(
typeTag: TypeTag<T>,
payload: T,
options: RequestOptions = RequestOptions.DEFAULT
options: RequestOptions = RequestOptions.DEFAULT,
): Response<Void> {
return this.resolveAsync(typeTag, payload, options).await()
}
Expand All @@ -117,7 +117,7 @@ suspend fun <T : Any> Client.AwakeableHandle.resolveSuspend(
*/
suspend inline fun <reified T : Any> Client.AwakeableHandle.resolveSuspend(
payload: T,
options: RequestOptions = RequestOptions.DEFAULT
options: RequestOptions = RequestOptions.DEFAULT,
): Response<Void> {
return this.resolveSuspend(typeTag<T>(), payload, options)
}
Expand All @@ -130,7 +130,7 @@ suspend inline fun <reified T : Any> Client.AwakeableHandle.resolveSuspend(
*/
suspend fun Client.AwakeableHandle.rejectSuspend(
reason: String,
options: RequestOptions = RequestOptions.DEFAULT
options: RequestOptions = RequestOptions.DEFAULT,
): Response<Void> {
return this.rejectAsync(reason, options).await()
}
Expand Down Expand Up @@ -180,7 +180,7 @@ suspend fun <T : Any?> Client.InvocationHandle<T>.getOutputSuspend(
*/
inline fun <reified Res> Client.idempotentInvocationHandle(
target: Target,
idempotencyKey: String
idempotencyKey: String,
): Client.IdempotentInvocationHandle<Res> {
return this.idempotentInvocationHandle(target, idempotencyKey, typeTag<Res>())
}
Expand Down Expand Up @@ -218,7 +218,7 @@ suspend fun <T> Client.IdempotentInvocationHandle<T>.getOutputSuspend(
*/
inline fun <reified Res> Client.workflowHandle(
workflowName: String,
workflowId: String
workflowId: String,
): Client.WorkflowHandle<Res> {
return this.workflowHandle(workflowName, workflowId, typeTag<Res>())
}
Expand Down
9 changes: 7 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
[libraries.junit-jupiter.version]
ref = 'junit'

[libraries.junit-platform-launcher]
module = 'org.junit.platform:junit-platform-launcher'
# TODO align this with ref = junit once bumped to junit 6
version = '1.14.2'

[libraries.kotlinx-coroutines-core]
module = 'org.jetbrains.kotlinx:kotlinx-coroutines-core'

Expand Down Expand Up @@ -209,7 +214,7 @@
jib = 'com.google.cloud.tools.jib:3.4.5'
jsonschema2pojo = 'org.jsonschema2pojo:1.2.2'
nexus-publish = 'io.github.gradle-nexus.publish-plugin:1.3.0'
openapi-generator = 'org.openapi.generator:7.5.0'
openapi-generator = 'org.openapi.generator:7.17.0'
protobuf = 'com.google.protobuf:0.9.4'
shadow = 'com.gradleup.shadow:9.0.0-beta8'
spotless = 'com.diffplug.spotless:7.2.1'
Expand All @@ -223,7 +228,7 @@

[versions]
jackson = '2.18.4'
junit = '5.10.2'
junit = '5.14.1'
kotlinx-coroutines = '1.10.2'
kotlinx-serialization = '1.9.0'
ksp = '2.2.10-2.0.2'
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
9 changes: 3 additions & 6 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ import kotlin.reflect.KClass
class KElementConverter(
private val logger: KSPLogger,
private val builtIns: KSBuiltIns,
private val byteArrayType: KSType
private val byteArrayType: KSType,
) : KSDefaultVisitor<Service.Builder, Unit>() {
companion object {
private val SUPPORTED_CLASS_KIND: Set<ClassKind> = setOf(ClassKind.CLASS, ClassKind.INTERFACE)
private val EMPTY_PAYLOAD: PayloadType =
PayloadType(
true, "", "Unit", "dev.restate.serde.kotlinx.KotlinSerializationSerdeFactory.UNIT")
true,
"",
"Unit",
"dev.restate.serde.kotlinx.KotlinSerializationSerdeFactory.UNIT",
)
private const val RAW_SERDE: String = "dev.restate.serde.Serde.RAW"
}

Expand All @@ -42,7 +46,8 @@ class KElementConverter(
override fun visitAnnotated(annotated: KSAnnotated, data: Service.Builder) {
if (annotated !is KSClassDeclaration) {
logger.error(
"Only classes or interfaces can be annotated with @Service or @VirtualObject or @Workflow")
"Only classes or interfaces can be annotated with @Service or @VirtualObject or @Workflow"
)
}
visitClassDeclaration(annotated as KSClassDeclaration, data)
}
Expand All @@ -56,7 +61,8 @@ class KElementConverter(
if (!SUPPORTED_CLASS_KIND.contains(classDeclaration.classKind)) {
logger.error(
"The ServiceProcessor supports only class declarations of kind $SUPPORTED_CLASS_KIND",
classDeclaration)
classDeclaration,
)
}
if (classDeclaration.getVisibility() == Visibility.PRIVATE) {
logger.error("The annotated class is private", classDeclaration)
Expand Down Expand Up @@ -94,7 +100,8 @@ class KElementConverter(
if (data.handlers.isEmpty()) {
logger.warn(
"The class declaration $targetFqcn has no methods annotated as handlers",
classDeclaration)
classDeclaration,
)
}

var serdeFactoryDecl = "dev.restate.serde.kotlinx.KotlinSerializationSerdeFactory()"
Expand Down Expand Up @@ -134,7 +141,8 @@ class KElementConverter(
if (!(!hasAnyAnnotation || hasExactlyOneAnnotation)) {
logger.error(
"You can have only one annotation between @Shared and @Exclusive and @Workflow to a method",
function)
function,
)
}

val handlerBuilder = Handler.builder()
Expand All @@ -158,7 +166,8 @@ class KElementConverter(
.withInputAccept(inputAcceptFromParameterList(function.parameters))
.withInputType(inputPayloadFromParameterList(function.parameters))
.withOutputType(outputPayloadFromExecutableElement(function))
.validateAndBuild())
.validateAndBuild()
)
} catch (e: Exception) {
logger.error("Error when building handler: $e", function)
}
Expand All @@ -184,7 +193,8 @@ class KElementConverter(
parameterElement.type.resolve(),
parameterElement.getAnnotationsByType(Json::class).firstOrNull(),
parameterElement.getAnnotationsByType(Raw::class).firstOrNull(),
parameterElement)
parameterElement,
)
}

@OptIn(KspExperimental::class)
Expand All @@ -193,14 +203,15 @@ class KElementConverter(
fn.returnType?.resolve() ?: builtIns.unitType,
fn.getAnnotationsByType(Json::class).firstOrNull(),
fn.getAnnotationsByType(Raw::class).firstOrNull(),
fn)
fn,
)
}

private fun payloadFromTypeMirrorAndAnnotations(
ty: KSType,
jsonAnnotation: Json?,
rawAnnotation: Raw?,
relatedNode: KSNode
relatedNode: KSNode,
): PayloadType {
if (ty == builtIns.unitType) {
if (rawAnnotation != null || jsonAnnotation != null) {
Expand All @@ -222,12 +233,16 @@ class KElementConverter(
val qualifiedTypeName = qualifiedTypeName(ty)
var serdeDecl: String =
if (rawAnnotation != null) RAW_SERDE else jsonSerdeDecl(ty, qualifiedTypeName)
if (rawAnnotation != null &&
rawAnnotation.contentType != getAnnotationDefaultValue(Raw::class.java, "contentType")) {
if (
rawAnnotation != null &&
rawAnnotation.contentType != getAnnotationDefaultValue(Raw::class.java, "contentType")
) {
serdeDecl = contentTypeDecoratedSerdeDecl(serdeDecl, rawAnnotation.contentType)
}
if (jsonAnnotation != null &&
jsonAnnotation.contentType != getAnnotationDefaultValue(Json::class.java, "contentType")) {
if (
jsonAnnotation != null &&
jsonAnnotation.contentType != getAnnotationDefaultValue(Json::class.java, "contentType")
) {
serdeDecl = contentTypeDecoratedSerdeDecl(serdeDecl, jsonAnnotation.contentType)
}

Expand All @@ -249,12 +264,13 @@ class KElementConverter(
private fun validateMethodSignature(
serviceType: ServiceType,
handlerType: HandlerType,
function: KSFunctionDeclaration
function: KSFunctionDeclaration,
) {
if (function.parameters.isEmpty()) {
logger.error(
"The annotated method has no parameters. There must be at least the context parameter as first parameter",
function)
function,
)
}
when (handlerType) {
HandlerType.SHARED ->
Expand All @@ -265,15 +281,17 @@ class KElementConverter(
} else {
logger.error(
"The annotation @Shared is not supported by the service type $serviceType",
function)
function,
)
}
HandlerType.EXCLUSIVE ->
if (serviceType == ServiceType.VIRTUAL_OBJECT) {
validateFirstParameterType(ObjectContext::class, function)
} else {
logger.error(
"The annotation @Exclusive is not supported by the service type $serviceType",
function)
function,
)
}
HandlerType.STATELESS -> validateFirstParameterType(Context::class, function)
HandlerType.WORKFLOW ->
Expand All @@ -282,17 +300,21 @@ class KElementConverter(
} else {
logger.error(
"The annotation @Workflow is not supported by the service type $serviceType",
function)
function,
)
}
}
}

private fun validateFirstParameterType(clazz: KClass<*>, function: KSFunctionDeclaration) {
if (function.parameters[0].type.resolve().declaration.qualifiedName!!.asString() !=
clazz.qualifiedName) {
if (
function.parameters[0].type.resolve().declaration.qualifiedName!!.asString() !=
clazz.qualifiedName
) {
logger.error(
"The method signature must have ${clazz.qualifiedName} as first parameter, was ${function.parameters[0].type.resolve().declaration.qualifiedName!!.asString()}",
function)
function,
)
}
}

Expand Down
Loading