Skip to content

Commit 6a554f7

Browse files
API followups (#633)
* Remove `to*` methods in java to go back to ``*handle` methods * Fix missing scope.toService etc in kotlin * Document additional env variable needed for virtual object scopes
1 parent 07b0b2b commit 6a554f7

11 files changed

Lines changed: 111 additions & 94 deletions

File tree

MIGRATION.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ one service at a time.
2929
| `ctx.key()` | `Restate.key()` |
3030
| `ctx.promise(key)` / `ctx.promiseHandle(key)` | `Restate.promise(key)` / `Restate.promiseHandle(key)` |
3131
| `ctx.invocationHandle(id, ...)` | `Restate.invocationHandle(id, ...)` |
32-
| Code-generated clients (Service) | `Restate.service(Class)` / `Restate.toService(Class)` |
33-
| Code-generated clients (Virtual Object) | `Restate.virtualObject(Class, key)` / `Restate.toVirtualObject(Class, key)` |
34-
| Code-generated clients (Workflow) | `Restate.workflow(Class, key)` / `Restate.toWorkflow(Class, key)` |
32+
| Code-generated clients (Service) | `Restate.service(Class)` / `Restate.serviceHandle(Class)` |
33+
| Code-generated clients (Virtual Object) | `Restate.virtualObject(Class, key)` / `Restate.virtualObjectHandle(Class, key)` |
34+
| Code-generated clients (Workflow) | `Restate.workflow(Class, key)` / `Restate.workflowHandle(Class, key)` |
3535

3636
From **outside** a handler (the ingress client), the equivalents live on `dev.restate.client.Client`:
37-
`client.service(Class)` / `client.toService(Class)` / `client.virtualObject(Class, key)` / etc.
37+
`client.service(Class)` / `client.serviceHandle(Class)` / `client.virtualObject(Class, key)` / etc.
3838

3939
### Kotlin: `Context` API → top-level functions
4040

@@ -129,16 +129,16 @@ Restate.virtualObject(Counter.class, "my-key").add(1);
129129

130130
```java
131131
// call() with a method reference returns a DurableFuture you can await and/or compose
132-
int count = Restate.toVirtualObject(Counter.class, "my-counter")
132+
int count = Restate.virtualObjectHandle(Counter.class, "my-counter")
133133
.call(Counter::increment)
134134
.await();
135135

136136
// send() for one-way invocation without waiting
137-
InvocationHandle<Integer> handle = Restate.toVirtualObject(Counter.class, "my-counter")
137+
InvocationHandle<Integer> handle = Restate.virtualObjectHandle(Counter.class, "my-counter")
138138
.send(Counter::increment);
139139

140140
// Invocation options such as an idempotency key
141-
int idempotentCount = Restate.toVirtualObject(Counter.class, "my-counter")
141+
int idempotentCount = Restate.virtualObjectHandle(Counter.class, "my-counter")
142142
.call(Counter::increment, InvocationOptions.idempotencyKey("my-idempotency-key"))
143143
.await();
144144
```

client-kotlin/src/main/kotlin/dev/restate/client/kotlin/ingress.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,12 @@ internal constructor(
330330
return service(client, SVC::class.java, scopeKey)
331331
}
332332

333-
/** @see Client.virtualObject */
333+
/**
334+
* *NOTE:* To use scopes with virtual objects you must enable the additional experimental feature
335+
* in restate-server, via `RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true`.
336+
*
337+
* @see Client.virtualObject
338+
*/
334339
@org.jetbrains.annotations.ApiStatus.Experimental
335340
inline fun <reified SVC : Any> virtualObject(key: String): SVC {
336341
return virtualObject(client, SVC::class.java, key, scopeKey)
@@ -352,7 +357,12 @@ internal constructor(
352357
return KClientRequestBuilder(client, SVC::class.java, null, scopeKey)
353358
}
354359

355-
/** @see Client.toVirtualObject */
360+
/**
361+
* *NOTE:* To use scopes with virtual objects you must enable the additional experimental feature
362+
* in restate-server, via `RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true`.
363+
*
364+
* @see Client.toVirtualObject
365+
*/
356366
@org.jetbrains.annotations.ApiStatus.Experimental
357367
inline fun <reified SVC : Any> toVirtualObject(key: String): KClientRequestBuilder<SVC> {
358368
ReflectionUtils.mustHaveVirtualObjectAnnotation(SVC::class.java)

client/src/main/java/dev/restate/client/Client.java

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -646,11 +646,11 @@ default <SVC> SVC service(Class<SVC> clazz) {
646646
* Client client = Client.connect("http://localhost:8080");
647647
*
648648
* // Use call() with method reference and wait for the result
649-
* Response<GreetingResponse> response = client.toService(Greeter.class)
649+
* Response<GreetingResponse> response = client.serviceHandle(Greeter.class)
650650
* .call(Greeter::greet, new Greeting("Alice"));
651651
*
652652
* // Use send() for one-way invocation without waiting
653-
* SendResponse<GreetingResponse> sendResponse = client.toService(Greeter.class)
653+
* SendResponse<GreetingResponse> sendResponse = client.serviceHandle(Greeter.class)
654654
* .send(Greeter::greet, new Greeting("Alice"));
655655
* }</pre>
656656
*
@@ -660,22 +660,14 @@ default <SVC> SVC service(Class<SVC> clazz) {
660660
* @param clazz the service class annotated with {@link Service}
661661
* @return a handle to invoke the service with advanced options
662662
*/
663-
default <SVC> ClientServiceHandle<SVC> toService(Class<SVC> clazz) {
663+
default <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
664664
ReflectionUtils.mustHaveServiceAnnotation(clazz);
665665
if (ReflectionUtils.isKotlinClass(clazz)) {
666666
throw new IllegalArgumentException("Using Kotlin classes with Java's API is not supported");
667667
}
668668
return new ClientServiceHandleImpl<>(this, clazz, null);
669669
}
670670

671-
/**
672-
* @deprecated Renamed to {@link #toService(Class)}.
673-
*/
674-
@Deprecated(since = "2.9", forRemoval = true)
675-
default <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
676-
return toService(clazz);
677-
}
678-
679671
/**
680672
* Simple API to invoke a Restate Virtual Object from the ingress.
681673
*
@@ -691,8 +683,8 @@ default <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
691683
* }</pre>
692684
*
693685
* <p>For advanced use cases requiring asynchronous request handling, access to {@link Response}
694-
* metadata, or invocation options (such as idempotency keys), use {@link #toVirtualObject(Class,
695-
* String)} instead.
686+
* metadata, or invocation options (such as idempotency keys), use {@link
687+
* #virtualObjectHandle(Class, String)} instead.
696688
*
697689
* @param clazz the virtual object class annotated with {@link VirtualObject}
698690
* @param key the key identifying the specific virtual object instance
@@ -736,11 +728,11 @@ default <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
736728
* Client client = Client.connect("http://localhost:8080");
737729
*
738730
* // Use call() with method reference and wait for the result
739-
* Response<Integer> response = client.toVirtualObject(Counter.class, "my-counter")
731+
* Response<Integer> response = client.virtualObjectHandle(Counter.class, "my-counter")
740732
* .call(Counter::increment);
741733
*
742734
* // Use send() for one-way invocation without waiting
743-
* SendResponse<Integer> sendResponse = client.toVirtualObject(Counter.class, "my-counter")
735+
* SendResponse<Integer> sendResponse = client.virtualObjectHandle(Counter.class, "my-counter")
744736
* .send(Counter::increment);
745737
* }</pre>
746738
*
@@ -751,22 +743,14 @@ default <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
751743
* @param key the key identifying the specific virtual object instance
752744
* @return a handle to invoke the virtual object with advanced options
753745
*/
754-
default <SVC> ClientServiceHandle<SVC> toVirtualObject(Class<SVC> clazz, String key) {
746+
default <SVC> ClientServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, String key) {
755747
ReflectionUtils.mustHaveVirtualObjectAnnotation(clazz);
756748
if (ReflectionUtils.isKotlinClass(clazz)) {
757749
throw new IllegalArgumentException("Using Kotlin classes with Java's API is not supported");
758750
}
759751
return new ClientServiceHandleImpl<>(this, clazz, key);
760752
}
761753

762-
/**
763-
* @deprecated Renamed to {@link #toVirtualObject(Class, String)}.
764-
*/
765-
@Deprecated(since = "2.9", forRemoval = true)
766-
default <SVC> ClientServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, String key) {
767-
return toVirtualObject(clazz, key);
768-
}
769-
770754
/**
771755
* Simple API to invoke a Restate Workflow from the ingress.
772756
*
@@ -782,7 +766,7 @@ default <SVC> ClientServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, Str
782766
* }</pre>
783767
*
784768
* <p>For advanced use cases requiring asynchronous request handling, access to {@link Response}
785-
* metadata, or invocation options (such as idempotency keys), use {@link #toWorkflow(Class,
769+
* metadata, or invocation options (such as idempotency keys), use {@link #workflowHandle(Class,
786770
* String)} instead.
787771
*
788772
* @param clazz the workflow class annotated with {@link Workflow}
@@ -827,11 +811,11 @@ default <SVC> SVC workflow(Class<SVC> clazz, String key) {
827811
* Client client = Client.connect("http://localhost:8080");
828812
*
829813
* // Use call() with method reference and wait for the result
830-
* Response<OrderResult> response = client.toWorkflow(OrderWorkflow.class, "order-123")
814+
* Response<OrderResult> response = client.workflowHandle(OrderWorkflow.class, "order-123")
831815
* .call(OrderWorkflow::start, new OrderRequest(...));
832816
*
833817
* // Use send() for one-way invocation without waiting
834-
* SendResponse<OrderResult> sendResponse = client.toWorkflow(OrderWorkflow.class, "order-123")
818+
* SendResponse<OrderResult> sendResponse = client.workflowHandle(OrderWorkflow.class, "order-123")
835819
* .send(OrderWorkflow::start, new OrderRequest(...));
836820
* }</pre>
837821
*
@@ -842,22 +826,14 @@ default <SVC> SVC workflow(Class<SVC> clazz, String key) {
842826
* @param key the key identifying the specific workflow instance
843827
* @return a handle to invoke the workflow with advanced options
844828
*/
845-
default <SVC> ClientServiceHandle<SVC> toWorkflow(Class<SVC> clazz, String key) {
829+
default <SVC> ClientServiceHandle<SVC> workflowHandle(Class<SVC> clazz, String key) {
846830
ReflectionUtils.mustHaveWorkflowAnnotation(clazz);
847831
if (ReflectionUtils.isKotlinClass(clazz)) {
848832
throw new IllegalArgumentException("Using Kotlin classes with Java's API is not supported");
849833
}
850834
return new ClientServiceHandleImpl<>(this, clazz, key);
851835
}
852836

853-
/**
854-
* @deprecated Renamed to {@link #toWorkflow(Class, String)}.
855-
*/
856-
@Deprecated(since = "2.9", forRemoval = true)
857-
default <SVC> ClientServiceHandle<SVC> workflowHandle(Class<SVC> clazz, String key) {
858-
return toWorkflow(clazz, key);
859-
}
860-
861837
/**
862838
* Create a default JDK client.
863839
*

client/src/main/java/dev/restate/client/ClientServiceHandle.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@
3434
* Client client = Client.connect("http://localhost:8080");
3535
*
3636
* // 1. Use call() with method reference and wait for the result
37-
* Response<GreetingResponse> response = client.toService(Greeter.class)
37+
* Response<GreetingResponse> response = client.serviceHandle(Greeter.class)
3838
* .call(Greeter::greet, new Greeting("Alice"));
3939
*
4040
* // 2. Use send() for one-way invocation without waiting
41-
* SendResponse<GreetingResponse> sendResponse = client.toService(Greeter.class)
41+
* SendResponse<GreetingResponse> sendResponse = client.serviceHandle(Greeter.class)
4242
* .send(Greeter::greet, new Greeting("Alice"));
4343
* }</pre>
4444
*
45-
* <p>Create instances using {@link Client#toService(Class)}, {@link Client#toVirtualObject(Class,
46-
* String)}, or {@link Client#toWorkflow(Class, String)}.
45+
* <p>Create instances using {@link Client#serviceHandle(Class)}, {@link
46+
* Client#virtualObjectHandle(Class, String)}, or {@link Client#workflowHandle(Class, String)}.
4747
*
4848
* <p>For simple synchronous request-response interactions returning just the output, consider using
4949
* the simple proxy API instead: {@link Client#service(Class)}, {@link Client#virtualObject(Class,

client/src/main/java/dev/restate/client/ScopedClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
7373
}
7474

7575
/**
76+
* <b>NOTE:</b> To use scopes with virtual objects you must enable the additional experimental
77+
* feature in restate-server, via {@code RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true}
78+
*
7679
* @see Client#virtualObject(Class, String)
7780
*/
7881
@org.jetbrains.annotations.ApiStatus.Experimental
@@ -100,6 +103,9 @@ public <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
100103
}
101104

102105
/**
106+
* <b>NOTE:</b> To use scopes with virtual objects you must enable the additional experimental
107+
* feature in restate-server, via {@code RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true}
108+
*
103109
* @see Client#virtualObjectHandle(Class, String)
104110
*/
105111
@org.jetbrains.annotations.ApiStatus.Experimental

examples/src/main/java/my/restate/sdk/examples/LoanWorkflow.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public String run(LoanRequest loanRequest) {
8484
Instant executionTime;
8585
try {
8686
executionTime =
87-
Restate.toService(MockBank.class)
87+
Restate.serviceHandle(MockBank.class)
8888
.call(
8989
MockBank::transfer,
9090
new TransferRequest(loanRequest.customerBankAccount(), loanRequest.amount()))
@@ -145,7 +145,7 @@ public static void main(String[] args) {
145145
LoanWorkflow loanWorkflow = restateClient.workflow(LoanWorkflow.class, "my-loan");
146146
var handle =
147147
restateClient
148-
.toWorkflow(LoanWorkflow.class, "my-loan")
148+
.workflowHandle(LoanWorkflow.class, "my-loan")
149149
.send(
150150
LoanWorkflow::run,
151151
new LoanRequest(

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ class KRequestBuilder<SVC : Any>
13611361
internal constructor(
13621362
private val clazz: Class<SVC>,
13631363
private val key: String?,
1364+
private val scope: String? = null,
13641365
) {
13651366
/**
13661367
* Create a request by invoking a method on the target.
@@ -1374,7 +1375,7 @@ internal constructor(
13741375
@Suppress("UNCHECKED_CAST")
13751376
suspend fun <Res> request(block: suspend SVC.() -> Res): KRequest<Any?, Res> {
13761377
return KRequestImpl(
1377-
RequestCaptureProxy(clazz, key).capture(block as suspend SVC.() -> Any?).toRequest()
1378+
RequestCaptureProxy(clazz, key, scope).capture(block as suspend SVC.() -> Any?).toRequest()
13781379
)
13791380
as KRequest<Any?, Res>
13801381
}
@@ -1542,7 +1543,12 @@ internal constructor(
15421543
return service(SVC::class.java, scopeKey)
15431544
}
15441545

1545-
/** @see virtualObject */
1546+
/**
1547+
* *NOTE:* To use scopes with virtual objects you must enable the additional experimental feature
1548+
* in restate-server, via `RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true`.
1549+
*
1550+
* @see virtualObject
1551+
*/
15461552
@org.jetbrains.annotations.ApiStatus.Experimental
15471553
suspend inline fun <reified SVC : Any> virtualObject(key: String): SVC {
15481554
return virtualObject(SVC::class.java, key, scopeKey)
@@ -1553,6 +1559,41 @@ internal constructor(
15531559
suspend inline fun <reified SVC : Any> workflow(key: String): SVC {
15541560
return workflow(SVC::class.java, key, scopeKey)
15551561
}
1562+
1563+
/** @see toService */
1564+
@org.jetbrains.annotations.ApiStatus.Experimental
1565+
inline fun <reified SVC : Any> toService(): KRequestBuilder<SVC> {
1566+
ReflectionUtils.mustHaveServiceAnnotation(SVC::class.java)
1567+
require(ReflectionUtils.isKotlinClass(SVC::class.java)) {
1568+
"Using Java classes with Kotlin's API is not supported"
1569+
}
1570+
return KRequestBuilder(SVC::class.java, null, scopeKey)
1571+
}
1572+
1573+
/**
1574+
* *NOTE:* To use scopes with virtual objects you must enable the additional experimental feature
1575+
* in restate-server, via `RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true`.
1576+
*
1577+
* @see toVirtualObject
1578+
*/
1579+
@org.jetbrains.annotations.ApiStatus.Experimental
1580+
inline fun <reified SVC : Any> toVirtualObject(key: String): KRequestBuilder<SVC> {
1581+
ReflectionUtils.mustHaveVirtualObjectAnnotation(SVC::class.java)
1582+
require(ReflectionUtils.isKotlinClass(SVC::class.java)) {
1583+
"Using Java classes with Kotlin's API is not supported"
1584+
}
1585+
return KRequestBuilder(SVC::class.java, key, scopeKey)
1586+
}
1587+
1588+
/** @see toWorkflow */
1589+
@org.jetbrains.annotations.ApiStatus.Experimental
1590+
inline fun <reified SVC : Any> toWorkflow(key: String): KRequestBuilder<SVC> {
1591+
ReflectionUtils.mustHaveWorkflowAnnotation(SVC::class.java)
1592+
require(ReflectionUtils.isKotlinClass(SVC::class.java)) {
1593+
"Using Java classes with Kotlin's API is not supported"
1594+
}
1595+
return KRequestBuilder(SVC::class.java, key, scopeKey)
1596+
}
15561597
}
15571598

15581599
/**

0 commit comments

Comments
 (0)