Skip to content

Commit d4505f7

Browse files
adamthom-amznsrchase
authored andcommitted
Generate per-operation ServiceHandlers (smithy-lang#304)
This lets service developers choose between a single lambda function per operation, or one lambda function that handles all operations, depending on their preferences.
1 parent dec26ee commit d4505f7

File tree

7 files changed

+238
-79
lines changed

7 files changed

+238
-79
lines changed

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/CodegenVisitor.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ public Void stringShape(StringShape shape) {
286286
return null;
287287
}
288288

289+
@Override
290+
public Void operationShape(OperationShape operation) {
291+
if (settings.generateServerSdk()) {
292+
writers.useShapeWriter(operation, serverSymbolProvider, w -> {
293+
ServerGenerator.generateOperationHandler(serverSymbolProvider, service, operation, w);
294+
});
295+
}
296+
return null;
297+
}
298+
289299
@Override
290300
public Void serviceShape(ServiceShape shape) {
291301
if (!Objects.equals(service, shape)) {
@@ -328,8 +338,13 @@ public Void serviceShape(ServiceShape shape) {
328338
protocolGenerator.generateResponseSerializers(serverContext);
329339
protocolGenerator.generateFrameworkErrorSerializer(serverContext);
330340
writers.useShapeWriter(shape, serverSymbolProvider, w -> {
331-
protocolGenerator.generateHandlerFactory(serverContext.withWriter(w));
341+
protocolGenerator.generateServiceHandlerFactory(serverContext.withWriter(w));
332342
});
343+
for (OperationShape operation: TopDownIndex.of(model).getContainedOperations(service)) {
344+
writers.useShapeWriter(operation, serverSymbolProvider, w -> {
345+
protocolGenerator.generateOperationHandlerFactory(serverContext.withWriter(w), operation);
346+
});
347+
}
333348
}
334349
protocolGenerator.generateSharedComponents(context);
335350
});

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerCommandGenerator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ private void writeOperationSerializer() {
142142
String serializerName = operationSymbol.expectProperty("serializerType", Symbol.class).getName();
143143
Symbol serverSymbol = symbolProvider.toSymbol(model.expectShape(settings.getService()));
144144

145-
writer.addImport("OperationSerializer", null, "@aws-smithy/server-common");
146-
writer.openBlock("export class $L implements OperationSerializer<$T, $S, $T> {", "}",
145+
writer.addImport("OperationSerializer", "__OperationSerializer", "@aws-smithy/server-common");
146+
writer.openBlock("export class $L implements __OperationSerializer<$T, $S, $T> {", "}",
147147
serializerName, serverSymbol, operation.getId().getName(), errorsType, () -> {
148148
String serializerFunction = ProtocolGenerator.getGenericSerFunctionName(operationSymbol) + "Response";
149149
String deserializerFunction = ProtocolGenerator.getGenericDeserFunctionName(operationSymbol) + "Request";

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerGenerator.java

Lines changed: 156 additions & 67 deletions
Large diffs are not rendered by default.

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerSymbolVisitor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ public Symbol operationShape(OperationShape shape) {
8383
builder.putProperty("outputType", intermediate.toBuilder().name(shapeName + "ServerOutput").build());
8484
builder.putProperty("errorsType", intermediate.toBuilder().name(shapeName + "Errors").build());
8585
builder.putProperty("serializerType", intermediate.toBuilder().name(shapeName + "Serializer").build());
86+
builder.putProperty("handler",
87+
intermediate.toBuilder().name(shapeName + "Handler").build());
8688
return builder.build();
8789
}
8890

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpBindingProtocolGenerator.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public void generateFrameworkErrorSerializer(GenerationContext inputContext) {
248248
writer.write("");
249249
}
250250

251-
private void generateMux(GenerationContext context) {
251+
private void generateServiceMux(GenerationContext context) {
252252
TopDownIndex topDownIndex = TopDownIndex.of(context.getModel());
253253
TypeScriptWriter writer = context.getWriter();
254254

@@ -273,6 +273,21 @@ private void generateMux(GenerationContext context) {
273273
);
274274
}
275275

276+
private void generateOperationMux(GenerationContext context, OperationShape operation) {
277+
TypeScriptWriter writer = context.getWriter();
278+
279+
writer.addImport("httpbinding", null, "@aws-smithy/server-common");
280+
281+
writer.openBlock("const mux = new httpbinding.HttpBindingMux<$S, $S>([", "]);",
282+
context.getService().getId().getName(),
283+
operation.getId().getName(),
284+
() -> {
285+
HttpTrait httpTrait = operation.expectTrait(HttpTrait.class);
286+
generateUriSpec(context, operation, httpTrait);
287+
}
288+
);
289+
}
290+
276291
private void generateUriSpec(GenerationContext context,
277292
OperationShape operation,
278293
HttpTrait httpTrait) {
@@ -321,7 +336,7 @@ private void generateUriSpec(GenerationContext context,
321336
}
322337

323338
@Override
324-
public void generateHandlerFactory(GenerationContext context) {
339+
public void generateServiceHandlerFactory(GenerationContext context) {
325340
TypeScriptWriter writer = context.getWriter();
326341
TopDownIndex index = TopDownIndex.of(context.getModel());
327342
Set<OperationShape> operations = index.getContainedOperations(context.getService());
@@ -334,11 +349,11 @@ public void generateHandlerFactory(GenerationContext context) {
334349
Symbol handlerSymbol = serviceSymbol.expectProperty("handler", Symbol.class);
335350
Symbol operationsSymbol = serviceSymbol.expectProperty("operations", Symbol.class);
336351

337-
writer.openBlock("export const get$L = (service: $T): ServiceHandler => {", "}",
352+
writer.openBlock("export const get$L = (service: $T): __ServiceHandler => {", "}",
338353
handlerSymbol.getName(), serviceSymbol, () -> {
339-
generateMux(context);
354+
generateServiceMux(context);
340355
writer.addImport("SmithyException", "__SmithyException", "@aws-sdk/smithy-client");
341-
writer.openBlock("const serFn: (op: $1T) => OperationSerializer<$2T, $1T, __SmithyException> = "
356+
writer.openBlock("const serFn: (op: $1T) => __OperationSerializer<$2T, $1T, __SmithyException> = "
342357
+ "(op) => {", "};", operationsSymbol, serviceSymbol, () -> {
343358
writer.openBlock("switch (op) {", "}", () -> {
344359
operations.stream()
@@ -350,6 +365,29 @@ public void generateHandlerFactory(GenerationContext context) {
350365
});
351366
}
352367

368+
@Override
369+
public void generateOperationHandlerFactory(GenerationContext context, OperationShape operation) {
370+
TypeScriptWriter writer = context.getWriter();
371+
SymbolProvider symbolProvider = context.getSymbolProvider();
372+
373+
writer.addImport("serializeFrameworkException", null,
374+
"./protocols/" + ProtocolGenerator.getSanitizedName(getName()));
375+
376+
final Symbol operationSymbol = symbolProvider.toSymbol(operation);
377+
final Symbol inputType = operationSymbol.expectProperty("inputType", Symbol.class);
378+
final Symbol outputType = operationSymbol.expectProperty("outputType", Symbol.class);
379+
final Symbol serializerType = operationSymbol.expectProperty("serializerType", Symbol.class);
380+
final Symbol operationHandlerSymbol = operationSymbol.expectProperty("handler", Symbol.class);
381+
382+
writer.openBlock("export const get$L = (operation: __Operation<$T, $T>): __ServiceHandler => {", "}",
383+
operationHandlerSymbol.getName(), inputType, outputType,
384+
() -> {
385+
generateOperationMux(context, operation);
386+
writer.write("return new $T(operation, mux, new $T(), serializeFrameworkException);",
387+
operationHandlerSymbol, serializerType);
388+
});
389+
}
390+
353391
private Consumer<OperationShape> writeOperationCase(
354392
TypeScriptWriter writer,
355393
SymbolProvider symbolProvider

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/HttpRpcProtocolGenerator.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,12 @@ public void generateResponseSerializers(GenerationContext context) {
159159
}
160160

161161
@Override
162-
public void generateHandlerFactory(GenerationContext context) {
162+
public void generateServiceHandlerFactory(GenerationContext context) {
163+
LOGGER.warning("Handler factory generation is not currently supported for RPC protocols.");
164+
}
165+
166+
@Override
167+
public void generateOperationHandlerFactory(GenerationContext context, OperationShape operation) {
163168
LOGGER.warning("Handler factory generation is not currently supported for RPC protocols.");
164169
}
165170

smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/integration/ProtocolGenerator.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import software.amazon.smithy.codegen.core.Symbol;
2323
import software.amazon.smithy.codegen.core.SymbolProvider;
2424
import software.amazon.smithy.model.Model;
25+
import software.amazon.smithy.model.shapes.OperationShape;
2526
import software.amazon.smithy.model.shapes.ServiceShape;
2627
import software.amazon.smithy.model.shapes.Shape;
2728
import software.amazon.smithy.model.shapes.ShapeId;
@@ -156,12 +157,21 @@ default void generateSharedComponents(GenerationContext context) {
156157
void generateFrameworkErrorSerializer(GenerationContext serverContext);
157158

158159
/**
159-
* Generates the code used to determine the service and operation
160-
* targeted by a given request.
160+
* Generates a factory for the ServiceHandler implementation for this service.
161161
*
162162
* @param context Generation context.
163163
*/
164-
void generateHandlerFactory(GenerationContext context);
164+
void generateServiceHandlerFactory(GenerationContext context);
165+
166+
/**
167+
* Generates the code used to handle a request for a specific operation in the given service. This allows the
168+
* business logic for a service to be split among multiple deployment targets, for example, one Lambda function
169+
* per operation.
170+
*
171+
* @param context Generation context.
172+
* @param operation The operation to generate a handler factory for.
173+
*/
174+
void generateOperationHandlerFactory(GenerationContext context, OperationShape operation);
165175

166176
/**
167177
* Generates the code used to deserialize the shapes of a service

0 commit comments

Comments
 (0)