Skip to content

Commit 2d6daea

Browse files
committed
generate service specific exception class for client sdk
1 parent d2a1497 commit 2d6daea

File tree

8 files changed

+172
-64
lines changed

8 files changed

+172
-64
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ static void writeServerIndex(
8888
writer.write("export * from \"./operations\";");
8989

9090
writer.write("export * from \"./$L\"", symbol.getName());
91-
9291
fileManifest.writeFile(
9392
Paths.get(CodegenUtils.SOURCE_FOLDER, ServerSymbolVisitor.SERVER_FOLDER, "index.ts").toString(),
9493
writer.toString());

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ private void renderStructureNamespace(StructuredMemberWriter structuredMemberWri
174174
}
175175

176176
/**
177-
* Error structures generate classes that extend from ServiceException
178-
* (ServiceException is case of server SDK), and add the appropriate fault
177+
* Error structures generate classes that extend from service base exception
178+
* (ServiceException in case of server SDK), and add the appropriate fault
179179
* property.
180180
*
181181
* <p>Given the following Smithy structure:
@@ -194,7 +194,7 @@ private void renderStructureNamespace(StructuredMemberWriter structuredMemberWri
194194
*
195195
* <pre>{@code
196196
* import { ExceptionOptionType as __ExceptionOptionType } from "@aws-sdk/smithy-client";
197-
* import { ServiceException as __BaseException } from "@aws-sdk/smithy-client";
197+
* import { FooServiceException as __BaseException } from "./FooServiceException";
198198
* // In server SDK:
199199
* // import { ServiceException as __BaseException } from "@aws-smithy/server-common";
200200
*
@@ -220,7 +220,7 @@ private void renderErrorStructure() {
220220
Symbol symbol = symbolProvider.toSymbol(shape);
221221
writer.writeShapeDocs(shape);
222222
boolean isServerSdk = this.includeValidation;
223-
writer.openBlock("export class $L extends $L {", symbol.getName(), "__BaseException");
223+
writer.openBlock("export class $T extends $L {", symbol, "__BaseException");
224224
writer.write("readonly name: $1S = $1S;", shape.getId().getName());
225225
writer.write("readonly $$fault: $1S = $1S;", errorTrait.getValue());
226226
if (!isServerSdk) {

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,7 @@ void writeMemberFilterSensitiveLog(TypeScriptWriter writer, MemberShape member,
129129
void writeErrorConstructor(TypeScriptWriter writer, Shape shape, boolean isServerSdk) {
130130
ErrorTrait errorTrait = shape.getTrait(ErrorTrait.class).orElseThrow(IllegalStateException::new);
131131
Symbol symbol = symbolProvider.toSymbol(shape);
132-
if (isServerSdk) {
133-
writer.addImport("ServiceException", "__BaseException", TypeScriptDependency.SERVER_COMMON.packageName);
134-
} else {
135-
writer.addImport("ServiceException", "__BaseException", TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
132+
if (!isServerSdk) {
136133
writer.writeDocs("@internal");
137134
}
138135
writer.addImport("ExceptionOptionType", "__ExceptionOptionType",

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

Lines changed: 0 additions & 48 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.smithy.typescript.codegen.integration;
17+
18+
import java.nio.file.Paths;
19+
import java.util.function.BiConsumer;
20+
import java.util.function.Consumer;
21+
import software.amazon.smithy.codegen.core.Symbol;
22+
import software.amazon.smithy.codegen.core.SymbolProvider;
23+
import software.amazon.smithy.codegen.core.SymbolReference;
24+
import software.amazon.smithy.model.Model;
25+
import software.amazon.smithy.model.shapes.ServiceShape;
26+
import software.amazon.smithy.model.traits.ErrorTrait;
27+
import software.amazon.smithy.typescript.codegen.CodegenUtils;
28+
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
29+
import software.amazon.smithy.typescript.codegen.TypeScriptSettings;
30+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
31+
import software.amazon.smithy.utils.SmithyInternalApi;
32+
33+
/**
34+
* Generate the base ServiceException class.
35+
*/
36+
@SmithyInternalApi
37+
public final class AddBaseServiceExceptionClass implements TypeScriptIntegration {
38+
39+
@Override
40+
public void writeAdditionalFiles(
41+
TypeScriptSettings settings,
42+
Model model,
43+
SymbolProvider symbolProvider,
44+
BiConsumer<String, Consumer<TypeScriptWriter>> writerFactory
45+
) {
46+
boolean isClientSdk = settings.generateClient();
47+
if (isClientSdk) {
48+
String serviceName = getServiceName(settings, model, symbolProvider);
49+
String serviceExceptionName = getServiceExceptionName(serviceName);
50+
writerFactory.accept(
51+
Paths.get(CodegenUtils.SOURCE_FOLDER, "models", serviceExceptionName + ".ts").toString(),
52+
writer -> {
53+
writer.addImport("ServiceException", "__ServiceException",
54+
TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
55+
writer.addImport("ServiceExceptionOptions", "__ServiceExceptionOptions",
56+
TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
57+
writer.writeDocs("Base exception class for all service exceptions from "
58+
+ serviceName + " service.");
59+
writer.openBlock("export class $L extends __ServiceException {", serviceExceptionName);
60+
writer.writeDocs("@internal");
61+
writer.openBlock("constructor(options: __ServiceExceptionOptions) {");
62+
writer.write("super(options);");
63+
writer.write("Object.setPrototypeOf(this, $L.prototype);", serviceExceptionName);
64+
writer.closeBlock("}"); // constructor
65+
writer.closeBlock("}"); // class
66+
});
67+
}
68+
}
69+
70+
@Override
71+
public void writeAdditionalExports(
72+
TypeScriptSettings settings,
73+
Model model,
74+
SymbolProvider symbolProvider,
75+
TypeScriptWriter writer
76+
) {
77+
boolean isClientSdk = settings.generateClient();
78+
if (isClientSdk) {
79+
String serviceName = getServiceName(settings, model, symbolProvider);
80+
String serviceExceptionName = getServiceExceptionName(serviceName);
81+
writer.write("export { $1L } from \"./models/$1L\";", serviceExceptionName);
82+
}
83+
}
84+
85+
/**
86+
* For any error shape, add the reference of the base error class to the
87+
* error symbol's references. In client SDK, the base error class is the
88+
* service-specific service exception class. In server SDK, the base error
89+
* class is the ServiceException class from server-common package.
90+
*/
91+
@Override
92+
public SymbolProvider decorateSymbolProvider(
93+
TypeScriptSettings settings,
94+
Model model,
95+
SymbolProvider symbolProvider
96+
) {
97+
return shape -> {
98+
Symbol symbol = symbolProvider.toSymbol(shape);
99+
if (shape.hasTrait(ErrorTrait.class)) {
100+
String serviceName = getServiceName(settings, model, symbolProvider);
101+
String baseExceptionAlias = "__BaseException";
102+
SymbolReference reference;
103+
if (settings.generateClient()) {
104+
String serviceExceptionName = getServiceExceptionName(serviceName);
105+
String namespace = Paths.get(".", "src", "models", serviceExceptionName).toString();
106+
Symbol serviceExceptionSymbol = Symbol.builder()
107+
.name(serviceExceptionName)
108+
.namespace(namespace, "/")
109+
.definitionFile(namespace + ".ts").build();
110+
reference = SymbolReference.builder()
111+
.options(SymbolReference.ContextOption.USE)
112+
.alias(baseExceptionAlias)
113+
.symbol(serviceExceptionSymbol)
114+
.build();
115+
} else {
116+
reference = SymbolReference.builder()
117+
.options(SymbolReference.ContextOption.USE)
118+
.alias(baseExceptionAlias)
119+
.symbol(TypeScriptDependency.SERVER_COMMON.createSymbol("ServiceException"))
120+
.build();
121+
}
122+
return symbol.toBuilder().addReference(reference).build();
123+
}
124+
return symbol;
125+
};
126+
}
127+
128+
private String getServiceName(
129+
TypeScriptSettings settings,
130+
Model model,
131+
SymbolProvider symbolProvider
132+
) {
133+
ServiceShape service = settings.getService(model);
134+
return symbolProvider.toSymbol(service).getName().replaceAll("(Client)$", "");
135+
}
136+
137+
private String getServiceExceptionName(String serviceName) {
138+
return serviceName + "ServiceException";
139+
}
140+
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2040,7 +2040,6 @@ private void generateOperationResponseDeserializer(
20402040
writer.write("return Promise.resolve(contents);");
20412041
});
20422042
writer.write("");
2043-
20442043
// Write out the error deserialization dispatcher.
20452044
Set<StructureShape> errorShapes = HttpProtocolGeneratorUtils.generateErrorDispatcher(
20462045
context, operation, responseType, this::writeErrorCodeParser,

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.smithy.typescript.codegen.integration;
1717

18+
import java.nio.file.Paths;
1819
import java.util.List;
1920
import java.util.Optional;
2021
import java.util.Set;
@@ -31,6 +32,7 @@
3132
import software.amazon.smithy.model.pattern.SmithyPattern;
3233
import software.amazon.smithy.model.shapes.MemberShape;
3334
import software.amazon.smithy.model.shapes.OperationShape;
35+
import software.amazon.smithy.model.shapes.ServiceShape;
3436
import software.amazon.smithy.model.shapes.Shape;
3537
import software.amazon.smithy.model.shapes.ShapeId;
3638
import software.amazon.smithy.model.shapes.StructureShape;
@@ -344,10 +346,9 @@ static Set<StructureShape> generateErrorDispatcher(
344346
});
345347
}
346348

347-
// Error responses must be at least ServiceException interface
348-
writer.addImport("ServiceException", "__ServiceException",
349-
TypeScriptDependency.AWS_SMITHY_CLIENT.packageName);
350-
writer.write("let response: __ServiceException;");
349+
// Error responses must be at least BaseException interface
350+
SymbolReference baseExceptionReference = getClientBaseException(context);
351+
writer.write("let response: $T;", baseExceptionReference);
351352
writer.write("let errorCode: string = \"UnknownError\";");
352353
errorCodeGenerator.accept(context);
353354
writer.openBlock("switch (errorCode) {", "}", () -> {
@@ -380,7 +381,7 @@ static Set<StructureShape> generateErrorDispatcher(
380381

381382
// Get the protocol specific error location for retrieving contents.
382383
String errorLocation = bodyErrorLocationModifier.apply(context, "parsedBody");
383-
writer.openBlock("response = new __ServiceException({", "});", () -> {
384+
writer.openBlock("response = new $T({", "});", baseExceptionReference, () -> {
384385
writer.write("name: $1L.code || $1L.Code || errorCode,", errorLocation);
385386
writer.write("$$fault: \"client\",");
386387
writer.write("$$metadata: deserializeMetadata(output)");
@@ -431,4 +432,24 @@ static void writeHostPrefix(GenerationContext context, OperationShape operation)
431432
});
432433
});
433434
}
435+
436+
/**
437+
* Construct a symbol reference of client's base exception class.
438+
*/
439+
private static SymbolReference getClientBaseException(GenerationContext context) {
440+
ServiceShape service = context.getService();
441+
SymbolProvider symbolProvider = context.getSymbolProvider();
442+
String serviceExceptionName = symbolProvider.toSymbol(service).getName()
443+
.replaceAll("(Client)$", "ServiceException");
444+
String namespace = Paths.get(".", "src", "models", serviceExceptionName).toString();
445+
Symbol serviceExceptionSymbol = Symbol.builder()
446+
.name(serviceExceptionName)
447+
.namespace(namespace, "/")
448+
.definitionFile(namespace + ".ts").build();
449+
return SymbolReference.builder()
450+
.options(SymbolReference.ContextOption.USE)
451+
.alias("__BaseException")
452+
.symbol(serviceExceptionSymbol)
453+
.build();
454+
}
434455
}

smithy-typescript-codegen/src/main/resources/META-INF/services/software.amazon.smithy.typescript.codegen.integration.TypeScriptIntegration

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ software.amazon.smithy.typescript.codegen.integration.AddEventStreamDependency
22
software.amazon.smithy.typescript.codegen.integration.AddChecksumRequiredDependency
33
software.amazon.smithy.typescript.codegen.integration.AddDefaultsModeDependency
44
software.amazon.smithy.typescript.codegen.integration.AddHttpApiKeyAuthPlugin
5-
software.amazon.smithy.typescript.codegen.integration.AddBaseExceptionClassExport
5+
software.amazon.smithy.typescript.codegen.integration.AddBaseServiceExceptionClass

0 commit comments

Comments
 (0)