Skip to content

Commit 1ee8b82

Browse files
Add API to configure services and handlers. This allows users to override documentation and metadata manually, without having to discover the service definition factory.
1 parent 2efcdf2 commit 1ee8b82

File tree

6 files changed

+250
-4
lines changed

6 files changed

+250
-4
lines changed

sdk-common/src/main/java/dev/restate/sdk/endpoint/Endpoint.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import dev.restate.sdk.endpoint.definition.ServiceDefinitionFactories;
1414
import io.opentelemetry.api.OpenTelemetry;
1515
import java.util.*;
16+
import java.util.function.Consumer;
1617
import java.util.function.Function;
1718
import java.util.stream.Collectors;
1819
import java.util.stream.Stream;
@@ -46,11 +47,14 @@ public static class Builder {
4647
* Add a Restate service to the endpoint. This will automatically discover the generated factory
4748
* based on the class name.
4849
*
50+
* <p>If you want to modify some of the service definition options, such as documentation,
51+
* inactivity timeout, and so on, use {@link #bind(Object, Consumer)} instead.
52+
*
4953
* <p>You can also manually instantiate the {@link ServiceDefinition} using {@link
5054
* #bind(ServiceDefinition)}.
5155
*/
5256
public Builder bind(Object service) {
53-
return this.bind(ServiceDefinitionFactories.discover(service).create(service, null));
57+
return this.bind(service, ignored -> {});
5458
}
5559

5660
/**
@@ -61,10 +65,45 @@ public Builder bind(Object service) {
6165
* <p>Look at the respective documentations of the HandlerRunner class in the Java or in the
6266
* Kotlin module.
6367
*
68+
* <p>If you want to modify some of the service definition options, such as documentation,
69+
* inactivity timeout, and so on, use {@link #bind(Object, HandlerRunner.Options, Consumer)}
70+
* instead.
71+
*
6472
* @see #bind(Object)
6573
*/
6674
public Builder bind(Object service, HandlerRunner.Options options) {
67-
return this.bind(ServiceDefinitionFactories.discover(service).create(service, options));
75+
return this.bind(service, options, ignored -> {});
76+
}
77+
78+
/**
79+
* Same as {@link #bind(Object)} but allows to configure the {@link ServiceDefinition} before
80+
* binding it.
81+
*
82+
* @see #bind(Object)
83+
* @see ServiceDefinition.Configurator
84+
*/
85+
public Builder bind(Object service, Consumer<ServiceDefinition.Configurator> configurator) {
86+
return this.bind(
87+
ServiceDefinitionFactories.discover(service)
88+
.create(service, null)
89+
.configure(configurator));
90+
}
91+
92+
/**
93+
* Same as {@link #bind(Object, HandlerRunner.Options)} but allows to configure the {@link
94+
* ServiceDefinition} before binding it.
95+
*
96+
* @see #bind(Object, HandlerRunner.Options)
97+
* @see ServiceDefinition.Configurator
98+
*/
99+
public Builder bind(
100+
Object service,
101+
HandlerRunner.Options options,
102+
Consumer<ServiceDefinition.Configurator> configurator) {
103+
return this.bind(
104+
ServiceDefinitionFactories.discover(service)
105+
.create(service, options)
106+
.configure(configurator));
68107
}
69108

70109
/** Add a manual {@link ServiceDefinition} to the endpoint. */
@@ -153,6 +192,23 @@ public static Builder bind(Object service, HandlerRunner.Options options) {
153192
return new Builder().bind(service, options);
154193
}
155194

195+
/**
196+
* @see Builder#bind(Object, Consumer)
197+
*/
198+
public static Builder bind(Object object, Consumer<ServiceDefinition.Configurator> configurator) {
199+
return new Builder().bind(object, configurator);
200+
}
201+
202+
/**
203+
* @see Builder#bind(Object, HandlerRunner.Options, Consumer)
204+
*/
205+
public static Builder bind(
206+
Object service,
207+
HandlerRunner.Options options,
208+
Consumer<ServiceDefinition.Configurator> configurator) {
209+
return new Builder().bind(service, options, configurator);
210+
}
211+
156212
/**
157213
* @see Builder#bind(ServiceDefinition)
158214
*/

sdk-common/src/main/java/dev/restate/sdk/endpoint/definition/HandlerDefinition.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
import dev.restate.serde.Serde;
1212
import java.util.Collections;
13+
import java.util.HashMap;
1314
import java.util.Map;
15+
import java.util.function.Consumer;
1416
import org.jspecify.annotations.Nullable;
1517

1618
public final class HandlerDefinition<REQ, RES> {
@@ -111,6 +113,83 @@ public HandlerDefinition<REQ, RES> withMetadata(Map<String, String> metadata) {
111113
runner);
112114
}
113115

116+
public HandlerDefinition<REQ, RES> configure(
117+
Consumer<HandlerDefinition.Configurator> configurator) {
118+
HandlerDefinition.Configurator configuratorObj =
119+
new HandlerDefinition.Configurator(acceptContentType, documentation, metadata);
120+
configurator.accept(configuratorObj);
121+
122+
return new HandlerDefinition<>(
123+
name,
124+
handlerType,
125+
configuratorObj.acceptContentType,
126+
requestSerde,
127+
responseSerde,
128+
configuratorObj.documentation,
129+
configuratorObj.metadata,
130+
runner);
131+
}
132+
133+
public static final class Configurator {
134+
135+
private @Nullable String acceptContentType;
136+
private @Nullable String documentation;
137+
private Map<String, String> metadata;
138+
139+
public Configurator(
140+
@Nullable String acceptContentType,
141+
@Nullable String documentation,
142+
Map<String, String> metadata) {
143+
this.acceptContentType = acceptContentType;
144+
this.documentation = documentation;
145+
this.metadata = new HashMap<>(metadata);
146+
}
147+
148+
public @Nullable String getAcceptContentType() {
149+
return acceptContentType;
150+
}
151+
152+
public void setAcceptContentType(@Nullable String acceptContentType) {
153+
this.acceptContentType = acceptContentType;
154+
}
155+
156+
public Configurator acceptContentType(@Nullable String acceptContentType) {
157+
this.setAcceptContentType(acceptContentType);
158+
return this;
159+
}
160+
161+
public @Nullable String getDocumentation() {
162+
return documentation;
163+
}
164+
165+
public void setDocumentation(@Nullable String documentation) {
166+
this.documentation = documentation;
167+
}
168+
169+
public Configurator documentation(@Nullable String documentation) {
170+
this.setDocumentation(documentation);
171+
return this;
172+
}
173+
174+
public Map<String, String> getMetadata() {
175+
return metadata;
176+
}
177+
178+
public void setMetadata(Map<String, String> metadata) {
179+
this.metadata = metadata;
180+
}
181+
182+
public Configurator addMetadata(String key, String value) {
183+
this.metadata.put(key, value);
184+
return this;
185+
}
186+
187+
public Configurator metadata(Map<String, String> metadata) {
188+
this.setMetadata(metadata);
189+
return this;
190+
}
191+
}
192+
114193
public static <REQ, RES> HandlerDefinition<REQ, RES> of(
115194
String handler,
116195
HandlerType handlerType,

sdk-common/src/main/java/dev/restate/sdk/endpoint/definition/ServiceDefinition.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package dev.restate.sdk.endpoint.definition;
1010

1111
import java.util.*;
12+
import java.util.function.Consumer;
1213
import java.util.function.Function;
1314
import java.util.stream.Collectors;
1415
import org.jspecify.annotations.Nullable;
@@ -66,6 +67,74 @@ public ServiceDefinition withMetadata(Map<String, String> metadata) {
6667
return new ServiceDefinition(serviceName, serviceType, handlers, documentation, metadata);
6768
}
6869

70+
public ServiceDefinition configure(Consumer<Configurator> configurator) {
71+
Configurator configuratorObj = new Configurator(handlers, documentation, metadata);
72+
configurator.accept(configuratorObj);
73+
74+
return new ServiceDefinition(
75+
serviceName,
76+
serviceType,
77+
configuratorObj.handlers,
78+
configuratorObj.documentation,
79+
configuratorObj.metadata);
80+
}
81+
82+
public static final class Configurator {
83+
84+
private Map<String, HandlerDefinition<?, ?>> handlers;
85+
private @Nullable String documentation;
86+
private Map<String, String> metadata;
87+
88+
private Configurator(
89+
Map<String, HandlerDefinition<?, ?>> handlers,
90+
@Nullable String documentation,
91+
Map<String, String> metadata) {
92+
this.handlers = new HashMap<>(handlers);
93+
this.documentation = documentation;
94+
this.metadata = new HashMap<>(metadata);
95+
}
96+
97+
public @Nullable String getDocumentation() {
98+
return documentation;
99+
}
100+
101+
public void setDocumentation(@Nullable String documentation) {
102+
this.documentation = documentation;
103+
}
104+
105+
public Configurator documentation(@Nullable String documentation) {
106+
this.setDocumentation(documentation);
107+
return this;
108+
}
109+
110+
public Map<String, String> getMetadata() {
111+
return metadata;
112+
}
113+
114+
public void setMetadata(Map<String, String> metadata) {
115+
this.metadata = metadata;
116+
}
117+
118+
public Configurator addMetadata(String key, String value) {
119+
this.metadata.put(key, value);
120+
return this;
121+
}
122+
123+
public Configurator metadata(Map<String, String> metadata) {
124+
this.setMetadata(metadata);
125+
return this;
126+
}
127+
128+
public Configurator configureHandler(
129+
String handlerName, Consumer<HandlerDefinition.Configurator> configurator) {
130+
if (!handlers.containsKey(handlerName)) {
131+
throw new IllegalArgumentException("Handler " + handlerName + " not found");
132+
}
133+
handlers.computeIfPresent(handlerName, (k, v) -> v.configure(configurator));
134+
return this;
135+
}
136+
}
137+
69138
@Override
70139
public boolean equals(Object object) {
71140
if (this == object) return true;

sdk-core/src/test/java/dev/restate/sdk/core/AssertUtils.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,18 @@ public static EndpointManifestSchemaAssert assertThatDiscovery(Object... service
8383
builder.bind(svc);
8484
}
8585

86+
return assertThatDiscovery(builder);
87+
}
88+
89+
public static EndpointManifestSchemaAssert assertThatDiscovery(Endpoint.Builder builder) {
90+
return assertThatDiscovery(builder.build());
91+
}
92+
93+
public static EndpointManifestSchemaAssert assertThatDiscovery(Endpoint endpoint) {
8694
return new EndpointManifestSchemaAssert(
8795
new EndpointManifest(
8896
EndpointManifestSchema.ProtocolMode.BIDI_STREAM,
89-
builder.build().getServiceDefinitions(),
97+
endpoint.getServiceDefinitions(),
9098
true)
9199
.manifest(),
92100
EndpointManifestSchemaAssert.class);

sdk-core/src/test/java/dev/restate/sdk/core/javaapi/CodegenDiscoveryTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import dev.restate.sdk.core.generated.manifest.Input;
1717
import dev.restate.sdk.core.generated.manifest.Output;
1818
import dev.restate.sdk.core.generated.manifest.Service;
19+
import dev.restate.sdk.endpoint.Endpoint;
1920
import org.junit.jupiter.api.Test;
2021

2122
public class CodegenDiscoveryTest {
@@ -66,4 +67,20 @@ void workflowType() {
6667
.extractingHandler("run")
6768
.returns(Handler.Ty.WORKFLOW, Handler::getTy);
6869
}
70+
71+
@Test
72+
void usingTransformer() {
73+
assertThatDiscovery(
74+
Endpoint.bind(
75+
new CodegenTest.RawInputOutput(),
76+
sd ->
77+
sd.documentation("My service documentation")
78+
.configureHandler(
79+
"rawInputWithCustomCt",
80+
hd -> hd.documentation("My handler documentation"))))
81+
.extractingService("RawInputOutput")
82+
.returns("My service documentation", Service::getDocumentation)
83+
.extractingHandler("rawInputWithCustomCt")
84+
.returns("My handler documentation", Handler::getDocumentation);
85+
}
6986
}

sdk-core/src/test/kotlin/dev/restate/sdk/core/kotlinapi/CodegenDiscoveryTest.kt

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

11-
import dev.restate.sdk.Context
1211
import dev.restate.sdk.core.AssertUtils.assertThatDiscovery
1312
import dev.restate.sdk.core.generated.manifest.Handler
1413
import dev.restate.sdk.core.generated.manifest.Input
1514
import dev.restate.sdk.core.generated.manifest.Output
1615
import dev.restate.sdk.core.generated.manifest.Service
16+
import dev.restate.sdk.kotlin.endpoint.*
1717
import org.assertj.core.api.Assertions
1818
import org.assertj.core.api.InstanceOfAssertFactories.type
1919
import org.junit.jupiter.api.Test
@@ -72,4 +72,21 @@ class CodegenDiscoveryTest {
7272
.extractingHandler("run")
7373
.returns(Handler.Ty.WORKFLOW) { obj -> obj.ty }
7474
}
75+
76+
@Test
77+
fun usingTransformer() {
78+
assertThatDiscovery(
79+
endpoint {
80+
bind(CodegenTest.RawInputOutput()) {
81+
it.documentation = "My service documentation"
82+
it.configureHandler("rawInputWithCustomCt") {
83+
it.documentation = "My handler documentation"
84+
}
85+
}
86+
})
87+
.extractingService("RawInputOutput")
88+
.returns("My service documentation", Service::getDocumentation)
89+
.extractingHandler("rawInputWithCustomCt")
90+
.returns("My handler documentation", Handler::getDocumentation)
91+
}
7592
}

0 commit comments

Comments
 (0)