Skip to content

Commit 1837734

Browse files
authored
generate server-side exceptions as classes (#502)
This change generates server-side exceptions as individual classes. So customers can handle exceptions with ease.
1 parent ffd510b commit 1837734

File tree

22 files changed

+466
-276
lines changed

22 files changed

+466
-276
lines changed

.github/CODEOWNERS

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ smithy-typescript-integ-tests/* @adamthom-amzn @gosar @JordonPhillips
99

1010
# These are all specific to SSDK functionality.
1111
smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerCommandGenerator.java @adamthom-amzn @gosar @JordonPhillips
12-
smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerErrorGenerator.java @adamthom-amzn @gosar @JordonPhillips
1312
smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerGenerator.java @adamthom-amzn @gosar @JordonPhillips
1413
smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/ServerSymbolVisitor.java @adamthom-amzn @gosar @JordonPhillips

config/checkstyle/checkstyle.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<!-- Files must contain a copyright header. -->
3333
<module name="RegexpHeader">
3434
<property name="header"
35-
value="/\*\n \* Copyright 20(19|20|21) Amazon\.com, Inc\. or its affiliates\. All Rights Reserved\.\n"/>
35+
value="/\*\n \* Copyright 20(19|20|21|22) Amazon\.com, Inc\. or its affiliates\. All Rights Reserved\.\n"/>
3636
<property name="fileExtensions" value="java"/>
3737
</module>
3838

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

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ public Void serviceShape(ServiceShape shape) {
370370

371371
if (settings.generateServerSdk()) {
372372
generateServiceInterface(shape);
373-
generateServerErrors(shape);
374373
}
375374

376375
if (protocolGenerator != null) {
@@ -470,20 +469,6 @@ private void generateServiceInterface(ServiceShape shape) {
470469
});
471470
}
472471

473-
private void generateServerErrors(ServiceShape service) {
474-
final OperationIndex operationIndex = OperationIndex.of(model);
475-
476-
TopDownIndex.of(model)
477-
.getContainedOperations(service)
478-
.stream()
479-
.flatMap(o -> operationIndex.getErrors(o, service).stream())
480-
.distinct()
481-
.sorted()
482-
.forEachOrdered(error -> writers.useShapeWriter(service, symbolProvider, writer -> {
483-
new ServerErrorGenerator(settings, model, error, symbolProvider, writer).run();
484-
}));
485-
}
486-
487472
private void generateCommands(ServiceShape shape) {
488473
// Generate each operation for the service.
489474
TopDownIndex topDownIndex = TopDownIndex.of(model);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,9 +741,9 @@ private void writeServerResponseTest(OperationShape operation, HttpResponseTestC
741741
writer.write("const request = new HttpRequest({method: 'POST', hostname: 'example.com'});");
742742

743743
// Create a new serializer factory that always returns our test serializer.
744-
writer.addImport("SmithyException", "__SmithyException", "@aws-sdk/types");
744+
writer.addImport("ServiceException", "__ServiceException", "@aws-smithy/server-common");
745745
writer.addImport("OperationSerializer", "__OperationSerializer", "@aws-smithy/server-common");
746-
writer.openBlock("const serFn: (op: $1T) => __OperationSerializer<$2T<{}>, $1T, __SmithyException> = (op) =>"
746+
writer.openBlock("const serFn: (op: $1T) => __OperationSerializer<$2T<{}>, $1T, __ServiceException> = (op) =>"
747747
+ " { return new TestSerializer(); };", serviceOperationsSymbol, serviceSymbol);
748748

749749
writer.addImport("serializeFrameworkException", null,

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ static void writeIndex(
6060

6161
// write export statement for models
6262
writer.write("export * from \"./models\";");
63+
64+
// Write each custom export.
65+
for (TypeScriptIntegration integration : integrations) {
66+
integration.writeAdditionalExports(settings, model, symbolProvider, writer);
67+
}
68+
6369
fileManifest.writeFile(Paths.get(CodegenUtils.SOURCE_FOLDER, "index.ts").toString(), writer.toString());
6470
}
6571

@@ -121,11 +127,5 @@ private static void writeClientExports(
121127
if (operations.stream().anyMatch(operation -> operation.hasTrait(WaitableTrait.ID))) {
122128
writer.write("export * from \"./waiters\";");
123129
}
124-
125-
// Write each custom export.
126-
for (TypeScriptIntegration integration : integrations) {
127-
integration.writeAdditionalExports(settings, model, symbolProvider, writer);
128-
}
129-
fileManifest.writeFile(Paths.get(CodegenUtils.SOURCE_FOLDER, "index.ts").toString(), writer.toString());
130130
}
131131
}

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

Lines changed: 0 additions & 77 deletions
This file was deleted.

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static void generateServiceHandler(SymbolProvider symbolProvider,
6363
writer.write("private readonly service: $T<Context>;", serviceSymbol);
6464
writer.write("private readonly mux: __Mux<$S, $T>;", serviceShape.getId().getName(), operationsType);
6565
writer.write("private readonly serializerFactory: <T extends $T>(operation: T) => "
66-
+ "__OperationSerializer<$T<Context>, T, __SmithyException>;",
66+
+ "__OperationSerializer<$T<Context>, T, __ServiceException>;",
6767
operationsType, serviceSymbol);
6868
writer.write("private readonly serializeFrameworkException: (e: __SmithyFrameworkException, "
6969
+ "ctx: __ServerSerdeContext) => Promise<__HttpResponse>;");
@@ -87,7 +87,7 @@ static void generateServiceHandler(SymbolProvider symbolProvider,
8787
writer.write("service: $T<Context>,", serviceSymbol);
8888
writer.write("mux: __Mux<$S, $T>,", serviceShape.getId().getName(), operationsType);
8989
writer.write("serializerFactory:<T extends $T>(op: T) => "
90-
+ "__OperationSerializer<$T<Context>, T, __SmithyException>,",
90+
+ "__OperationSerializer<$T<Context>, T, __ServiceException>,",
9191
operationsType, serviceSymbol);
9292
writer.write("serializeFrameworkException: (e: __SmithyFrameworkException, ctx: __ServerSerdeContext) "
9393
+ "=> Promise<__HttpResponse>,");
@@ -208,7 +208,7 @@ private static void addCommonHandlerImports(TypeScriptWriter writer) {
208208
writer.addImport("SmithyFrameworkException", "__SmithyFrameworkException", "@aws-smithy/server-common");
209209
writer.addImport("HttpRequest", "__HttpRequest", "@aws-sdk/protocol-http");
210210
writer.addImport("HttpResponse", "__HttpResponse", "@aws-sdk/protocol-http");
211-
writer.addImport("SmithyException", "__SmithyException", "@aws-sdk/types");
211+
writer.addImport("ServiceException", "__ServiceException", "@aws-smithy/server-common");
212212
writer.addImport("ValidationCustomizer", "__ValidationCustomizer", "@aws-smithy/server-common");
213213
}
214214

@@ -226,7 +226,7 @@ private static void writeHandleFunction(TypeScriptWriter writer) {
226226
writer.write("request: __HttpRequest,");
227227
writer.write("context: Context,");
228228
writer.write("operationName: O,");
229-
writer.write("serializer: __OperationSerializer<S, O, __SmithyException>,");
229+
writer.write("serializer: __OperationSerializer<S, O, __ServiceException>,");
230230
writer.write("operation: __Operation<__OperationInput<S[O]>, __OperationOutput<S[O]>, Context>,");
231231
writer.write("serializeFrameworkException: (e: __SmithyFrameworkException, "
232232
+ "ctx: __ServerSerdeContext) => Promise<__HttpResponse>,");

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

Lines changed: 64 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import java.util.List;
2222
import java.util.stream.Collectors;
23-
import java.util.stream.Stream;
2423
import software.amazon.smithy.codegen.core.Symbol;
2524
import software.amazon.smithy.codegen.core.SymbolProvider;
2625
import software.amazon.smithy.codegen.core.SymbolReference;
@@ -140,61 +139,6 @@ private void renderNonErrorStructure() {
140139
renderStructureNamespace(config, includeValidation);
141140
}
142141

143-
/**
144-
* Error structures generate interfaces that extend from SmithyException
145-
* and add the appropriate fault property.
146-
*
147-
* <p>Given the following Smithy structure:
148-
*
149-
* <pre>{@code
150-
* namespace smithy.example
151-
*
152-
* @error("client")
153-
* structure NoSuchResource {
154-
* @required
155-
* resourceType: String
156-
* }
157-
* }</pre>
158-
*
159-
* <p>The following TypeScript is generated:
160-
*
161-
* <pre>{@code
162-
* import {
163-
* SmithyException as __SmithyException
164-
* } from "@aws-sdk/types";
165-
*
166-
* export interface NoSuchResource extends __SmithyException, $MetadataBearer {
167-
* name: "NoSuchResource";
168-
* $fault: "client";
169-
* resourceType: string | undefined;
170-
* }
171-
* }</pre>
172-
*/
173-
private void renderErrorStructure() {
174-
ErrorTrait errorTrait = shape.getTrait(ErrorTrait.class).orElseThrow(IllegalStateException::new);
175-
Symbol symbol = symbolProvider.toSymbol(shape);
176-
writer.writeShapeDocs(shape);
177-
178-
// Find symbol references with the "extends" property, and add SmithyException.
179-
writer.addImport("SmithyException", "__SmithyException", "@aws-sdk/types");
180-
String extendsFrom = Stream.concat(
181-
Stream.of("__SmithyException"),
182-
symbol.getReferences().stream()
183-
.filter(ref -> ref.getProperty(SymbolVisitor.IMPLEMENTS_INTERFACE_PROPERTY).isPresent())
184-
.map(SymbolReference::getAlias)
185-
).collect(Collectors.joining(", "));
186-
187-
writer.openBlock("export interface $L extends $L {", symbol.getName(), extendsFrom);
188-
writer.write("name: $S;", shape.getId().getName());
189-
writer.write("$$fault: $S;", errorTrait.getValue());
190-
HttpProtocolGeneratorUtils.writeRetryableTrait(writer, shape, ";");
191-
StructuredMemberWriter structuredMemberWriter = new StructuredMemberWriter(
192-
model, symbolProvider, shape.getAllMembers().values());
193-
structuredMemberWriter.writeMembers(writer, shape);
194-
writer.closeBlock("}"); // interface
195-
writer.write("");
196-
}
197-
198142
private void renderStructureNamespace(StructuredMemberWriter structuredMemberWriter, boolean includeValidation) {
199143
Symbol symbol = symbolProvider.toSymbol(shape);
200144
writer.openBlock("export namespace $L {", "}", symbol.getName(), () -> {
@@ -228,4 +172,68 @@ private void renderStructureNamespace(StructuredMemberWriter structuredMemberWri
228172
});
229173
});
230174
}
175+
176+
/**
177+
* Error structures generate classes that extend from service base exception
178+
* (ServiceException in case of server SDK), and add the appropriate fault
179+
* property.
180+
*
181+
* <p>Given the following Smithy structure:
182+
*
183+
* <pre>{@code
184+
* namespace smithy.example
185+
*
186+
* @error("client")
187+
* structure NoSuchResource {
188+
* @required
189+
* resourceType: String
190+
* }
191+
* }</pre>
192+
*
193+
* <p>The following TypeScript is generated:
194+
*
195+
* <pre>{@code
196+
* import { ExceptionOptionType as __ExceptionOptionType } from "@aws-sdk/smithy-client";
197+
* import { FooServiceException as __BaseException } from "./FooServiceException";
198+
* // In server SDK:
199+
* // import { ServiceException as __BaseException } from "@aws-smithy/server-common";
200+
*
201+
* export class NoSuchResource extends __BaseException {
202+
* name: "NoSuchResource";
203+
* $fault: "client";
204+
* resourceType: string | undefined;
205+
* // @internal
206+
* constructor(opts: __ExceptionOptionType<NoSuchResource, __BaseException>) {
207+
* super({
208+
* name: "NoSuchResource",
209+
* $fault: "client",
210+
* ...opts
211+
* });
212+
* Object.setPrototypeOf(this, NoSuchResource.prototype);
213+
* this.resourceType = opts.resourceType;
214+
* }
215+
* }
216+
* }</pre>
217+
*/
218+
private void renderErrorStructure() {
219+
ErrorTrait errorTrait = shape.getTrait(ErrorTrait.class).orElseThrow(IllegalStateException::new);
220+
Symbol symbol = symbolProvider.toSymbol(shape);
221+
writer.writeShapeDocs(shape);
222+
boolean isServerSdk = this.includeValidation;
223+
writer.openBlock("export class $T extends $L {", symbol, "__BaseException");
224+
writer.write("readonly name: $1S = $1S;", shape.getId().getName());
225+
writer.write("readonly $$fault: $1S = $1S;", errorTrait.getValue());
226+
if (!isServerSdk) {
227+
HttpProtocolGeneratorUtils.writeRetryableTrait(writer, shape, ";");
228+
}
229+
StructuredMemberWriter structuredMemberWriter = new StructuredMemberWriter(model, symbolProvider,
230+
shape.getAllMembers().values());
231+
// since any error interface must extend from JavaScript Error interface, message member is already
232+
// required in the JavaScript Error interface
233+
structuredMemberWriter.skipMembers.add("message");
234+
structuredMemberWriter.writeMembers(writer, shape);
235+
structuredMemberWriter.writeErrorConstructor(writer, shape, isServerSdk);
236+
writer.closeBlock("}");
237+
writer.write("");
238+
}
231239
}

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,29 @@ void writeMemberFilterSensitiveLog(TypeScriptWriter writer, MemberShape member,
124124
}
125125

126126
/**
127-
* Writes a constructor function that takes in an object allowing modeled fields to be initialized.
127+
* Writes constructor of SDK exception classes.
128128
*/
129-
void writeConstructor(TypeScriptWriter writer, Shape shape) {
130-
writer.openBlock("constructor(opts: {", "}) {", () -> {
131-
writeMembers(writer, shape);
129+
void writeErrorConstructor(TypeScriptWriter writer, Shape shape, boolean isServerSdk) {
130+
ErrorTrait errorTrait = shape.getTrait(ErrorTrait.class).orElseThrow(IllegalStateException::new);
131+
Symbol symbol = symbolProvider.toSymbol(shape);
132+
if (!isServerSdk) {
133+
writer.writeDocs("@internal");
134+
}
135+
writer.addImport("ExceptionOptionType", "__ExceptionOptionType",
136+
TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
137+
writer.openBlock("constructor(opts: __ExceptionOptionType<$L, __BaseException>) {", symbol.getName());
138+
writer.openBlock("super({", "});", () -> {
139+
writer.write("name: $S,", shape.getId().getName());
140+
writer.write("$$fault: $S,", errorTrait.getValue());
141+
writer.write("...opts");
132142
});
133-
writer.indent();
134-
143+
writer.write("Object.setPrototypeOf(this, $L.prototype);", symbol.getName());
135144
for (MemberShape member : members) {
136145
if (skipMembers.contains(member.getMemberName())) {
137146
continue;
138147
}
139-
140148
writer.write("this.${1L} = opts.${1L};", getSanitizedMemberName(member));
141149
}
142-
143150
writer.closeBlock("}");
144151
}
145152

0 commit comments

Comments
 (0)