Skip to content

Commit 1db85f7

Browse files
Reintroduce some old APIs to reduce breakage. (#490)
* Reintroduce some old APIs to reduce breakage. * Javadocs improvements
1 parent 755e166 commit 1db85f7

File tree

15 files changed

+503
-21
lines changed

15 files changed

+503
-21
lines changed

sdk-api-gen-common/src/main/java/dev/restate/sdk/gen/model/Service.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public List<Handler> getHandlers() {
166166

167167
public Service validateAndBuild() {
168168
String restateName =
169-
this.restateName != null
169+
(this.restateName != null && !this.restateName.isBlank())
170170
? this.restateName
171171
: Objects.requireNonNull(simpleClassGeneratedNamePrefix);
172172
String serviceNameLowercase = restateName.toLowerCase();

sdk-api-gen/src/main/java/dev/restate/sdk/gen/ElementConverter.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,32 @@ Service fromTypeElement(MetaRestateAnnotation metaAnnotation, TypeElement elemen
6262
String serviceName =
6363
Optional.ofNullable(element.getAnnotation(dev.restate.sdk.annotation.Name.class))
6464
.map(Name::value)
65-
.orElse(null);
65+
.orElseGet(
66+
() -> {
67+
// Find annotation mirror
68+
AnnotationMirror metaAnnotationMirror =
69+
element.getAnnotationMirrors().stream()
70+
.filter(
71+
a ->
72+
a.getAnnotationType()
73+
.asElement()
74+
.equals(metaAnnotation.getAnnotationTypeElement()))
75+
.findFirst()
76+
.orElseThrow(
77+
() ->
78+
new IllegalStateException(
79+
"Cannot find the annotation mirror for meta annotation "
80+
+ metaAnnotation
81+
.getAnnotationTypeElement()
82+
.getQualifiedName()));
83+
84+
String name = metaAnnotation.resolveName(metaAnnotationMirror);
85+
86+
if (name != null && !name.isBlank()) {
87+
return name;
88+
}
89+
return null;
90+
});
6691

6792
// Compute handlers
6893
List<Handler> handlers =

sdk-api-gen/src/main/resources/templates/Handlers.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{{#if originalClassPkg}}package {{originalClassPkg}};{{/if}}
22

3+
@SuppressWarnings("unchecked")
34
public final class {{generatedClassSimpleName}} {
45

56
private {{generatedClassSimpleName}}() {}

sdk-api-kotlin-gen/src/main/kotlin/dev/restate/sdk/kotlin/gen/KElementConverter.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,17 @@ class KElementConverter(
6868
// Use simple class name, flattening subclasses names
6969
val inCodeServiceName =
7070
targetFqcn.substring(targetPkg.length).replace(Pattern.quote(".").toRegex(), "")
71+
72+
classDeclaration.getAnnotationsByType(Name::class).firstOrNull().let {
73+
if (it != null) {
74+
data.withRestateName(it.value)
75+
}
76+
}
77+
7178
data
7279
.withTargetClassPkg(targetPkg)
7380
.withTargetClassFqcn(targetFqcn)
7481
.withGeneratedClassesNamePrefix(inCodeServiceName)
75-
.withRestateName(classDeclaration.getAnnotationsByType(Name::class).firstOrNull()?.value)
7682

7783
// Compute handlersMetadata
7884
classDeclaration

sdk-api-kotlin-gen/src/main/kotlin/dev/restate/sdk/kotlin/gen/MetaRestateAnnotation.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,18 @@
88
// https://github.com/restatedev/sdk-java/blob/main/LICENSE
99
package dev.restate.sdk.kotlin.gen
1010

11+
import com.google.devtools.ksp.symbol.KSAnnotated
1112
import com.google.devtools.ksp.symbol.KSName
1213
import dev.restate.sdk.endpoint.definition.ServiceType
1314

14-
internal data class MetaRestateAnnotation(val annotationName: KSName, val serviceType: ServiceType)
15+
internal data class MetaRestateAnnotation(
16+
val annotationName: KSName,
17+
val serviceType: ServiceType
18+
) {
19+
fun resolveName(annotated: KSAnnotated): String? =
20+
annotated.annotations
21+
.find { it.annotationType.resolve().declaration.qualifiedName == annotationName }
22+
?.arguments
23+
?.firstOrNull { it -> it.name?.getShortName() == "name" }
24+
?.value as String?
25+
}

sdk-api-kotlin-gen/src/main/kotlin/dev/restate/sdk/kotlin/gen/ServiceProcessor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class ServiceProcessor(
8181
.map {
8282
val serviceBuilder = Service.builder()
8383
serviceBuilder.withServiceType(it.first.serviceType)
84+
serviceBuilder.withRestateName(it.first.resolveName(it.second))
8485

8586
converter.visitAnnotated(it.second, serviceBuilder)
8687

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH
2+
//
3+
// This file is part of the Restate Java SDK,
4+
// which is released under the MIT license.
5+
//
6+
// You can find a copy of the license in file LICENSE in the root
7+
// directory of this repository or package, or at
8+
// https://github.com/restatedev/sdk-java/blob/main/LICENSE
9+
package dev.restate.sdk.kotlin
10+
11+
import dev.restate.common.Slice
12+
import dev.restate.sdk.common.DurablePromiseKey
13+
import dev.restate.sdk.common.StateKey
14+
import dev.restate.serde.Serde
15+
import dev.restate.serde.Serde.Schema
16+
import java.nio.charset.StandardCharsets
17+
import kotlin.reflect.typeOf
18+
import kotlinx.serialization.ExperimentalSerializationApi
19+
import kotlinx.serialization.KSerializer
20+
import kotlinx.serialization.Serializable
21+
import kotlinx.serialization.builtins.ListSerializer
22+
import kotlinx.serialization.builtins.serializer
23+
import kotlinx.serialization.descriptors.PrimitiveKind
24+
import kotlinx.serialization.descriptors.SerialDescriptor
25+
import kotlinx.serialization.descriptors.StructureKind
26+
import kotlinx.serialization.encodeToString
27+
import kotlinx.serialization.json.Json
28+
import kotlinx.serialization.json.JsonArray
29+
import kotlinx.serialization.json.JsonElement
30+
import kotlinx.serialization.json.JsonNull
31+
import kotlinx.serialization.json.JsonTransformingSerializer
32+
import kotlinx.serialization.serializer
33+
34+
@Deprecated("Use stateKey() instead")
35+
object KtStateKey {
36+
37+
/** Creates a json [StateKey]. */
38+
@Deprecated("Use stateKey() instead", replaceWith = ReplaceWith(expression = "stateKey()"))
39+
inline fun <reified T> json(name: String): StateKey<T> {
40+
return StateKey.of(name, KtSerdes.json())
41+
}
42+
}
43+
44+
@Deprecated("Use durablePromiseKey() instead")
45+
object KtDurablePromiseKey {
46+
47+
/** Creates a json [StateKey]. */
48+
@Deprecated(
49+
"Use durablePromiseKey() instead",
50+
replaceWith = ReplaceWith(expression = "durablePromiseKey()"))
51+
inline fun <reified T : Any?> json(name: String): DurablePromiseKey<T> {
52+
return DurablePromiseKey.of(name, KtSerdes.json())
53+
}
54+
}
55+
56+
@Deprecated("Moved to dev.restate.serde.kotlinx")
57+
object KtSerdes {
58+
59+
@Deprecated("Moved to dev.restate.serde.kotlinx")
60+
inline fun <reified T : Any?> json(): Serde<T> {
61+
@Suppress("UNCHECKED_CAST")
62+
return when (typeOf<T>()) {
63+
typeOf<Unit>() -> UNIT as Serde<T>
64+
else -> json(serializer())
65+
}
66+
}
67+
68+
@Deprecated("Moved to dev.restate.serde.kotlinx")
69+
val UNIT: Serde<Unit> =
70+
object : Serde<Unit> {
71+
override fun serialize(value: Unit?): Slice {
72+
return Slice.EMPTY
73+
}
74+
75+
override fun deserialize(value: Slice) {
76+
return
77+
}
78+
79+
override fun contentType(): String? {
80+
return null
81+
}
82+
}
83+
84+
@Deprecated("Moved to dev.restate.serde.kotlinx")
85+
inline fun <reified T : Any?> json(serializer: KSerializer<T>): Serde<T> {
86+
return object : Serde<T> {
87+
override fun serialize(value: T?): Slice {
88+
if (value == null) {
89+
return Slice.wrap(
90+
Json.encodeToString(JsonNull.serializer(), JsonNull).encodeToByteArray())
91+
}
92+
93+
return Slice.wrap(Json.encodeToString(serializer, value).encodeToByteArray())
94+
}
95+
96+
override fun deserialize(value: Slice): T {
97+
return Json.decodeFromString(
98+
serializer, String(value.toByteArray(), StandardCharsets.UTF_8))
99+
}
100+
101+
override fun contentType(): String {
102+
return "application/json"
103+
}
104+
105+
override fun jsonSchema(): Schema {
106+
val schema: JsonSchema = serializer.descriptor.jsonSchema()
107+
return Serde.StringifiedJsonSchema(Json.encodeToString(schema))
108+
}
109+
}
110+
}
111+
112+
@Deprecated("Moved to dev.restate.serde.kotlinx")
113+
@Serializable
114+
@PublishedApi
115+
internal data class JsonSchema(
116+
@Serializable(with = StringListSerializer::class) val type: List<String>? = null,
117+
val format: String? = null,
118+
) {
119+
companion object {
120+
val INT = JsonSchema(type = listOf("number"), format = "int32")
121+
122+
val LONG = JsonSchema(type = listOf("number"), format = "int64")
123+
124+
val DOUBLE = JsonSchema(type = listOf("number"), format = "double")
125+
126+
val FLOAT = JsonSchema(type = listOf("number"), format = "float")
127+
128+
val STRING = JsonSchema(type = listOf("string"))
129+
130+
val BOOLEAN = JsonSchema(type = listOf("boolean"))
131+
132+
val OBJECT = JsonSchema(type = listOf("object"))
133+
134+
val LIST = JsonSchema(type = listOf("array"))
135+
136+
val ANY = JsonSchema()
137+
}
138+
}
139+
140+
object StringListSerializer :
141+
JsonTransformingSerializer<List<String>>(ListSerializer(String.Companion.serializer())) {
142+
override fun transformSerialize(element: JsonElement): JsonElement {
143+
require(element is JsonArray)
144+
return element.singleOrNull() ?: element
145+
}
146+
}
147+
148+
@Deprecated("Moved to dev.restate.serde.kotlinx")
149+
@OptIn(ExperimentalSerializationApi::class)
150+
@PublishedApi
151+
internal fun SerialDescriptor.jsonSchema(): JsonSchema {
152+
var schema =
153+
when (this.kind) {
154+
PrimitiveKind.BOOLEAN -> JsonSchema.BOOLEAN
155+
PrimitiveKind.BYTE -> JsonSchema.INT
156+
PrimitiveKind.CHAR -> JsonSchema.STRING
157+
PrimitiveKind.DOUBLE -> JsonSchema.DOUBLE
158+
PrimitiveKind.FLOAT -> JsonSchema.FLOAT
159+
PrimitiveKind.INT -> JsonSchema.INT
160+
PrimitiveKind.LONG -> JsonSchema.LONG
161+
PrimitiveKind.SHORT -> JsonSchema.INT
162+
PrimitiveKind.STRING -> JsonSchema.STRING
163+
StructureKind.LIST -> JsonSchema.LIST
164+
StructureKind.MAP -> JsonSchema.OBJECT
165+
else -> JsonSchema.ANY
166+
}
167+
168+
// Add nullability constraint
169+
if (this.isNullable && schema.type != null) {
170+
schema = schema.copy(type = schema.type.plus("null"))
171+
}
172+
173+
return schema
174+
}
175+
}

sdk-api-kotlin/src/main/kotlin/dev/restate/sdk/kotlin/api.kt

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,9 @@ sealed interface Context {
9191
): InvocationHandle<Res>
9292

9393
/**
94-
* Execute a non-deterministic closure, recording the result value in the journal. The result
95-
* value will be re-played in case of re-invocation (e.g. because of failure recovery or
96-
* suspension point) without re-executing the closure. Use this feature if you want to perform
97-
* <b>non-deterministic operations</b>.
94+
* Execute a closure, recording the result value in the journal. The result value will be
95+
* re-played in case of re-invocation (e.g. because of failure recovery or suspension point)
96+
* without re-executing the closure.
9897
*
9998
* You can name this closure using the `name` parameter. This name will be available in the
10099
* observability tools.
@@ -146,6 +145,22 @@ sealed interface Context {
146145
return runAsync(typeTag, name, retryPolicy, block).await()
147146
}
148147

148+
/**
149+
* Execute a closure asynchronously. This is like [runBlock], but it returns a [DurableFuture]
150+
* that you can combine and select.
151+
*
152+
* ```
153+
* // Fan out the subtasks - run them in parallel
154+
* val futures = subTasks.map { subTask ->
155+
* ctx.runAsync { subTask.execute() }
156+
* }
157+
*
158+
* // Fan in - Await all results and aggregate
159+
* val results = futures.awaitAll()
160+
* ```
161+
*
162+
* @see runBlock
163+
*/
149164
suspend fun <T : Any?> runAsync(
150165
typeTag: TypeTag<T>,
151166
name: String = "",
@@ -203,10 +218,12 @@ inline fun <reified Res : Any?> Context.invocationHandle(
203218
}
204219

205220
/**
206-
* Execute a non-deterministic closure, recording the result value in the journal. The result value
207-
* will be re-played in case of re-invocation (e.g. because of failure recovery or suspension point)
208-
* without re-executing the closure. Use this feature if you want to perform <b>non-deterministic
209-
* operations</b>.
221+
* Execute a closure, recording the result value in the journal. The result value will be re-played
222+
* in case of re-invocation (e.g. because of failure recovery or suspension point) without
223+
* re-executing the closure.
224+
*
225+
* You can name this closure using the `name` parameter. This name will be available in the
226+
* observability tools.
210227
*
211228
* <p>The closure should tolerate retries, that is Restate might re-execute the closure multiple
212229
* times until it records a result. To control and limit the amount of retries, pass a [RetryPolicy]
@@ -240,6 +257,7 @@ inline fun <reified Res : Any?> Context.invocationHandle(
240257
*
241258
* To propagate failures to the run call-site, make sure to wrap them in [TerminalException].
242259
*
260+
* @param name the name of the side effect.
243261
* @param block closure to execute.
244262
* @param T type of the return value.
245263
* @return value of the runBlock operation.
@@ -252,6 +270,22 @@ suspend inline fun <reified T : Any> Context.runBlock(
252270
return this.runBlock(typeTag<T>(), name, retryPolicy, block)
253271
}
254272

273+
/**
274+
* Execute a closure asynchronously. This is like [runBlock], but it returns a [DurableFuture] that
275+
* you can combine and select.
276+
*
277+
* ```
278+
* // Fan out the subtasks - run them in parallel
279+
* val futures = subTasks.map { subTask ->
280+
* ctx.runAsync { subTask.execute() }
281+
* }
282+
*
283+
* // Fan in - Await all results and aggregate
284+
* val results = futures.awaitAll()
285+
* ```
286+
*
287+
* @see runBlock
288+
*/
255289
suspend inline fun <reified T : Any> Context.runAsync(
256290
name: String = "",
257291
retryPolicy: RetryPolicy? = null,
@@ -641,6 +675,14 @@ sealed interface DurablePromise<T> {
641675
/** @return the future to await the promise result on. */
642676
suspend fun future(): DurableFuture<T>
643677

678+
@Deprecated(
679+
message = "Use future() instead",
680+
level = DeprecationLevel.WARNING,
681+
replaceWith = ReplaceWith(expression = "future()"))
682+
suspend fun awaitable(): DurableFuture<T> {
683+
return future()
684+
}
685+
644686
/** @return the value, if already present, otherwise returns an empty optional. */
645687
suspend fun peek(): Output<T>
646688
}

sdk-api/src/main/java/dev/restate/sdk/Context.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ default DurableFuture<Void> timer(Duration duration) {
143143
DurableFuture<Void> timer(String name, Duration duration);
144144

145145
/**
146-
* Execute a non-deterministic closure, recording the result value in the journal. The result
147-
* value will be re-played in case of re-invocation (e.g. because of failure recovery or
148-
* suspension point) without re-executing the closure. Use this feature if you want to perform
149-
* <b>non-deterministic operations</b>.
146+
* Execute a closure, recording the result value in the journal. The result value will be
147+
* re-played in case of re-invocation (e.g. because of failure recovery or suspension point)
148+
* without re-executing the closure.
150149
*
151150
* <pre>{@code
152151
* String result = ctx.run(
@@ -310,8 +309,8 @@ default void run(ThrowingRunnable runnable) throws TerminalException {
310309
}
311310

312311
/**
313-
* Execute a non-deterministic action asynchronously. This is like {@link #run(String, Class,
314-
* ThrowingSupplier)}, but it returns a {@link DurableFuture} that you can combine and select.
312+
* Execute a closure asynchronously. This is like {@link #run(String, Class, ThrowingSupplier)},
313+
* but it returns a {@link DurableFuture} that you can combine and select.
315314
*
316315
* <pre>{@code
317316
* // Fan-out

0 commit comments

Comments
 (0)