Skip to content

Commit 3596e08

Browse files
committed
Add service schema to clients
Clients now get access to the schema of the service, providing the shape ID and traits of the service allowed for code generation. This can be used to know what service is being called for telemetry, or implement things like endpoint resolvers or custom auth handling based on traits applied to a service. Each generated ApiOperation now provides a getter called `service()` to get the generated ApiService for the client. Resources get this method too. The generated ApiService only provides the schema of the service. It doesn't provide a full accounting of every operation or resource within the service as there isn't a clear use for that, it adds complexity due to bidirectional references, and we want to avoid eagerly loading every shape in a service when the ApiService is loaded. This commit also simplifies the generated ApiResource for this same reason, removing the operation and sub-resource schemas. DynamicClient will now automatically generate an ApiService based on the Smithy model.
1 parent 3d67f2a commit 3596e08

File tree

31 files changed

+400
-176
lines changed

31 files changed

+400
-176
lines changed

client/client-core/src/main/java/software/amazon/smithy/java/client/core/Client.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,9 @@ protected Client(Builder<?, ?> builder) {
4747
}
4848

4949
this.config = configBuilder.build();
50-
5150
this.pipeline = ClientPipeline.of(config.protocol(), config.transport());
52-
53-
// TODO: Add an interceptor to throw service-specific exceptions (e.g., PersonDirectoryClientException).
5451
this.interceptor = ClientInterceptor.chain(config.interceptors());
55-
5652
this.identityResolvers = IdentityResolvers.of(config.identityResolvers());
57-
5853
this.typeRegistry = typeRegistry();
5954

6055
if (config.retryStrategy() != null) {
@@ -184,7 +179,9 @@ public <T> B putConfigIfAbsent(Context.Key<T> key, T value) {
184179
*/
185180
@SuppressWarnings("unchecked")
186181
public B withConfiguration(ClientConfig config) {
187-
configBuilder.transport(config.transport())
182+
configBuilder
183+
.service(config.service())
184+
.transport(config.transport())
188185
.protocol(config.protocol())
189186
.endpointResolver(config.endpointResolver())
190187
.authSchemeResolver(config.authSchemeResolver())

client/client-core/src/main/java/software/amazon/smithy/java/client/core/ClientConfig.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import software.amazon.smithy.java.client.core.endpoint.EndpointResolver;
2020
import software.amazon.smithy.java.client.core.interceptors.ClientInterceptor;
2121
import software.amazon.smithy.java.context.Context;
22+
import software.amazon.smithy.java.core.schema.ApiService;
2223
import software.amazon.smithy.java.logging.InternalLogger;
2324
import software.amazon.smithy.java.retries.api.RetryStrategy;
2425

@@ -48,6 +49,7 @@ public final class ClientConfig {
4849
private final List<IdentityResolver<?>> identityResolvers;
4950
private final Context context;
5051
private final Set<Class<? extends ClientPlugin>> appliedPlugins;
52+
private final ApiService service;
5153

5254
private final RetryStrategy retryStrategy;
5355
private final String retryScope;
@@ -86,6 +88,7 @@ private ClientConfig(Builder builder) {
8688

8789
this.context = Context.unmodifiableCopy(builder.context);
8890
this.appliedPlugins = Collections.unmodifiableSet(new LinkedHashSet<>(builder.appliedPlugins));
91+
this.service = Objects.requireNonNull(builder.service, "Missing required service schema");
8992
}
9093

9194
/**
@@ -113,6 +116,15 @@ public Set<Class<? extends ClientPlugin>> appliedPlugins() {
113116
return appliedPlugins;
114117
}
115118

119+
/**
120+
* Get the configured service schema.
121+
*
122+
* @return the service schema.
123+
*/
124+
public ApiService service() {
125+
return service;
126+
}
127+
116128
/**
117129
* @return Transport for client to use to send data to an endpoint.
118130
*/
@@ -244,6 +256,7 @@ private void applyOverrides(Builder builder, RequestOverrideConfig overrideConfi
244256
* Static builder for ClientConfiguration.
245257
*/
246258
public static final class Builder {
259+
private ApiService service;
247260
private ClientTransport<?, ?> transport;
248261
private ClientProtocol<?, ?> protocol;
249262
private EndpointResolver endpointResolver;
@@ -258,6 +271,7 @@ public static final class Builder {
258271

259272
private Builder copyBuilder() {
260273
Builder builder = new Builder();
274+
builder.service = service;
261275
builder.transport = transport;
262276
builder.protocol = protocol;
263277
builder.endpointResolver = endpointResolver;
@@ -272,6 +286,13 @@ private Builder copyBuilder() {
272286
return builder;
273287
}
274288

289+
/**
290+
* @return Get the service schema.
291+
*/
292+
public ApiService service() {
293+
return service;
294+
}
295+
275296
/**
276297
* @return Get the transport.
277298
*/
@@ -342,6 +363,17 @@ public String retryScope() {
342363
return retryScope;
343364
}
344365

366+
/**
367+
* Set the service schema.
368+
*
369+
* @param service The service schema.
370+
* @return Returns the builder.
371+
*/
372+
public Builder service(ApiService service) {
373+
this.service = service;
374+
return this;
375+
}
376+
345377
/**
346378
* Set the transport used to send requests.
347379
*

client/client-core/src/test/java/software/amazon/smithy/java/client/core/endpoint/EndpointResolverTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.hamcrest.Matchers;
1212
import org.junit.jupiter.api.Test;
1313
import software.amazon.smithy.java.core.schema.ApiOperation;
14+
import software.amazon.smithy.java.core.schema.ApiService;
1415
import software.amazon.smithy.java.core.schema.PreludeSchemas;
1516
import software.amazon.smithy.java.core.schema.Schema;
1617
import software.amazon.smithy.java.core.schema.SerializableStruct;
@@ -155,6 +156,11 @@ public TypeRegistry errorRegistry() {
155156
public List<ShapeId> effectiveAuthSchemes() {
156157
throw new UnsupportedOperationException();
157158
}
159+
160+
@Override
161+
public ApiService service() {
162+
return null;
163+
}
158164
}
159165

160166
private static final class TestOperationStaticPrefix implements
@@ -191,6 +197,11 @@ public Schema outputSchema() {
191197
throw new UnsupportedOperationException();
192198
}
193199

200+
@Override
201+
public ApiService service() {
202+
return null;
203+
}
204+
194205
@Override
195206
public TypeRegistry errorRegistry() {
196207
throw new UnsupportedOperationException();

client/client-core/src/test/java/software/amazon/smithy/java/client/core/interceptors/TestStructs.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.List;
99
import software.amazon.smithy.java.core.schema.ApiOperation;
10+
import software.amazon.smithy.java.core.schema.ApiService;
1011
import software.amazon.smithy.java.core.schema.PreludeSchemas;
1112
import software.amazon.smithy.java.core.schema.Schema;
1213
import software.amazon.smithy.java.core.schema.SerializableStruct;
@@ -54,6 +55,11 @@ public TypeRegistry errorRegistry() {
5455
public List<ShapeId> effectiveAuthSchemes() {
5556
return List.of();
5657
}
58+
59+
@Override
60+
public ApiService service() {
61+
return null;
62+
}
5763
};
5864

5965
static final class Foo implements SerializableStruct {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.client.core.pagination.models;
7+
8+
import software.amazon.smithy.java.core.schema.ApiService;
9+
import software.amazon.smithy.java.core.schema.Schema;
10+
import software.amazon.smithy.model.shapes.ShapeId;
11+
12+
public final class PaginationService implements ApiService {
13+
@Override
14+
public Schema schema() {
15+
return Schema.createService(ShapeId.from("smithy.example#Pagination"));
16+
}
17+
}

client/client-core/src/test/java/software/amazon/smithy/java/client/core/pagination/models/TestOperationPaginated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.List;
99
import software.amazon.smithy.java.core.schema.ApiOperation;
10+
import software.amazon.smithy.java.core.schema.ApiService;
1011
import software.amazon.smithy.java.core.schema.Schema;
1112
import software.amazon.smithy.java.core.schema.ShapeBuilder;
1213
import software.amazon.smithy.java.core.serde.TypeRegistry;
@@ -57,4 +58,9 @@ public TypeRegistry errorRegistry() {
5758
public List<ShapeId> effectiveAuthSchemes() {
5859
throw new UnsupportedOperationException();
5960
}
61+
62+
@Override
63+
public ApiService service() {
64+
return new PaginationService();
65+
}
6066
}

client/client-http/src/test/java/software/amazon/smithy/java/client/http/JavaHttpClientTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import software.amazon.smithy.java.aws.client.awsjson.AwsJson1Protocol;
2020
import software.amazon.smithy.java.client.core.ClientConfig;
2121
import software.amazon.smithy.java.client.core.endpoint.EndpointResolver;
22+
import software.amazon.smithy.java.core.schema.Schema;
2223
import software.amazon.smithy.model.shapes.ShapeId;
2324

2425
public class JavaHttpClientTest {
@@ -82,6 +83,8 @@ public void automaticallyAppliesUserAgentPlugin() throws URISyntaxException {
8283
builder.protocol(new AwsJson1Protocol(ShapeId.from("foo#Bar")));
8384
builder.transport(new JavaHttpClientTransport());
8485
builder.endpointResolver(EndpointResolver.staticEndpoint(new URI("localhost:8080")));
86+
var serviceSchema = Schema.createService(ShapeId.from("foo#Bar"));
87+
builder.service(() -> serviceSchema);
8588
var config = builder.build();
8689

8790
assertThat(config.interceptors(), not(empty()));

client/client-http/src/test/java/software/amazon/smithy/java/client/http/plugins/UserAgentPluginTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import software.amazon.smithy.java.client.core.interceptors.RequestHook;
2121
import software.amazon.smithy.java.context.Context;
2222
import software.amazon.smithy.java.core.schema.ApiOperation;
23+
import software.amazon.smithy.java.core.schema.ApiService;
2324
import software.amazon.smithy.java.core.schema.Schema;
2425
import software.amazon.smithy.java.core.schema.SerializableStruct;
2526
import software.amazon.smithy.java.core.schema.ShapeBuilder;
@@ -135,6 +136,11 @@ public Schema outputSchema() {
135136
return null;
136137
}
137138

139+
@Override
140+
public ApiService service() {
141+
return null;
142+
}
143+
138144
@Override
139145
public TypeRegistry errorRegistry() {
140146
return null;

client/client-waiters/src/test/java/software/amazon/smithy/java/client/waiters/models/TestOperationWaitable.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.List;
99
import software.amazon.smithy.java.core.schema.ApiOperation;
10+
import software.amazon.smithy.java.core.schema.ApiService;
1011
import software.amazon.smithy.java.core.schema.Schema;
1112
import software.amazon.smithy.java.core.schema.ShapeBuilder;
1213
import software.amazon.smithy.java.core.serde.TypeRegistry;
@@ -49,4 +50,9 @@ public TypeRegistry errorRegistry() {
4950
public List<ShapeId> effectiveAuthSchemes() {
5051
throw new UnsupportedOperationException();
5152
}
53+
54+
@Override
55+
public ApiService service() {
56+
return new WaiterApiService();
57+
}
5258
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.java.client.waiters.models;
7+
8+
import software.amazon.smithy.java.core.schema.ApiService;
9+
import software.amazon.smithy.java.core.schema.Schema;
10+
import software.amazon.smithy.model.shapes.ShapeId;
11+
12+
public final class WaiterApiService implements ApiService {
13+
@Override
14+
public Schema schema() {
15+
return Schema.createService(ShapeId.from("smithy.example#Waiter"));
16+
}
17+
}

client/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicClient.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import software.amazon.smithy.java.core.error.CallException;
2626
import software.amazon.smithy.java.core.error.ModeledException;
2727
import software.amazon.smithy.java.core.schema.ApiOperation;
28+
import software.amazon.smithy.java.core.schema.ApiService;
2829
import software.amazon.smithy.java.core.schema.Schema;
2930
import software.amazon.smithy.java.core.schema.SerializableStruct;
3031
import software.amazon.smithy.java.core.serde.TypeRegistry;
@@ -70,11 +71,11 @@ public final class DynamicClient extends Client {
7071
private final Map<String, OperationShape> operationNames = new HashMap<>();
7172
private final TypeRegistry serviceErrorRegistry;
7273

73-
private DynamicClient(Builder builder, ServiceShape shape, Model model) {
74+
private DynamicClient(Builder builder, SchemaConverter converter, ServiceShape shape, Model model) {
7475
super(builder);
7576
this.model = model;
7677
this.service = shape;
77-
this.schemaConverter = new SchemaConverter(model);
78+
this.schemaConverter = converter;
7879

7980
// Create a lookup table of operation names to the operation shape IDs.
8081
for (var operation : TopDownIndex.of(model).getContainedOperations(service)) {
@@ -234,7 +235,7 @@ private ApiOperation<WrappedDocument, WrappedDocument> getApiOperation(String na
234235
}
235236

236237
return new DynamicOperation(
237-
service.getId(),
238+
config().service(),
238239
operationSchema,
239240
inputSchema,
240241
outputSchema,
@@ -264,7 +265,19 @@ public DynamicClient build() {
264265
autoDetectProtocol();
265266
}
266267

267-
return new DynamicClient(this, shape, model);
268+
var converter = new SchemaConverter(model);
269+
270+
// Create the schema for the service and put it in the config.
271+
var serviceSchema = converter.getSchema(shape);
272+
var apiService = new ApiService() {
273+
@Override
274+
public Schema schema() {
275+
return serviceSchema;
276+
}
277+
};
278+
configBuilder().service(apiService);
279+
280+
return new DynamicClient(this, converter, shape, model);
268281
}
269282

270283
@SuppressWarnings("unchecked")

client/dynamic-client/src/main/java/software/amazon/smithy/java/dynamicclient/DynamicOperation.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.List;
99
import java.util.Set;
1010
import software.amazon.smithy.java.core.schema.ApiOperation;
11+
import software.amazon.smithy.java.core.schema.ApiService;
1112
import software.amazon.smithy.java.core.schema.Schema;
1213
import software.amazon.smithy.java.core.schema.ShapeBuilder;
1314
import software.amazon.smithy.java.core.schema.TraitKey;
@@ -18,7 +19,7 @@
1819

1920
final class DynamicOperation implements ApiOperation<WrappedDocument, WrappedDocument> {
2021

21-
private final ShapeId service;
22+
private final ApiService service;
2223
private final Schema operationSchema;
2324
private final Schema inputSchema;
2425
private final Schema outputSchema;
@@ -27,7 +28,7 @@ final class DynamicOperation implements ApiOperation<WrappedDocument, WrappedDoc
2728
private final List<ShapeId> effectiveAuthSchemes;
2829

2930
public DynamicOperation(
30-
ShapeId service,
31+
ApiService service,
3132
Schema operationSchema,
3233
Schema inputSchema,
3334
Schema outputSchema,
@@ -59,6 +60,11 @@ public Schema schema() {
5960
return operationSchema;
6061
}
6162

63+
@Override
64+
public ApiService service() {
65+
return service;
66+
}
67+
6268
@Override
6369
public Schema inputSchema() {
6470
return inputSchema;
@@ -71,12 +77,12 @@ public Schema outputSchema() {
7177

7278
@Override
7379
public ShapeBuilder<WrappedDocument> inputBuilder() {
74-
return SchemaConverter.createDocumentBuilder(inputSchema(), service);
80+
return SchemaConverter.createDocumentBuilder(inputSchema(), service.schema().id());
7581
}
7682

7783
@Override
7884
public ShapeBuilder<WrappedDocument> outputBuilder() {
79-
return SchemaConverter.createDocumentBuilder(outputSchema(), service);
85+
return SchemaConverter.createDocumentBuilder(outputSchema(), service.schema().id());
8086
}
8187

8288
@Override

0 commit comments

Comments
 (0)