Skip to content

Commit a264562

Browse files
authored
fix awsjson error deserialization to not expect string code (#2489)
1 parent 6ae62c2 commit a264562

File tree

142 files changed

+33740
-53137
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+33740
-53137
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
{
2+
"id": "ee9db7c3-4c79-4670-9eba-3af651c48631",
3+
"type": "bugfix",
4+
"collapse": true,
5+
"description": "Correct failure to determine the error type in awsJson services that could occur when errors were modeled with a non-string `code` field.",
6+
"modules": [
7+
"service/acm",
8+
"service/acmpca",
9+
"service/alexaforbusiness",
10+
"service/applicationautoscaling",
11+
"service/applicationdiscoveryservice",
12+
"service/applicationinsights",
13+
"service/apprunner",
14+
"service/appstream",
15+
"service/athena",
16+
"service/autoscalingplans",
17+
"service/b2bi",
18+
"service/backupgateway",
19+
"service/bcmdataexports",
20+
"service/budgets",
21+
"service/cloud9",
22+
"service/cloudcontrol",
23+
"service/cloudhsm",
24+
"service/cloudhsmv2",
25+
"service/cloudtrail",
26+
"service/cloudwatchevents",
27+
"service/cloudwatchlogs",
28+
"service/codebuild",
29+
"service/codecommit",
30+
"service/codedeploy",
31+
"service/codepipeline",
32+
"service/codestar",
33+
"service/codestarconnections",
34+
"service/cognitoidentity",
35+
"service/cognitoidentityprovider",
36+
"service/comprehend",
37+
"service/comprehendmedical",
38+
"service/computeoptimizer",
39+
"service/configservice",
40+
"service/costandusagereportservice",
41+
"service/costexplorer",
42+
"service/costoptimizationhub",
43+
"service/databasemigrationservice",
44+
"service/datapipeline",
45+
"service/datasync",
46+
"service/dax",
47+
"service/devicefarm",
48+
"service/directconnect",
49+
"service/directoryservice",
50+
"service/dynamodb",
51+
"service/dynamodbstreams",
52+
"service/ec2instanceconnect",
53+
"service/ecr",
54+
"service/ecrpublic",
55+
"service/ecs",
56+
"service/emr",
57+
"service/eventbridge",
58+
"service/firehose",
59+
"service/fms",
60+
"service/forecast",
61+
"service/forecastquery",
62+
"service/frauddetector",
63+
"service/freetier",
64+
"service/fsx",
65+
"service/gamelift",
66+
"service/globalaccelerator",
67+
"service/glue",
68+
"service/health",
69+
"service/healthlake",
70+
"service/identitystore",
71+
"service/inspector",
72+
"service/iotfleetwise",
73+
"service/iotsecuretunneling",
74+
"service/iotthingsgraph",
75+
"service/kendra",
76+
"service/kendraranking",
77+
"service/keyspaces",
78+
"service/kinesis",
79+
"service/kinesisanalytics",
80+
"service/kinesisanalyticsv2",
81+
"service/kms",
82+
"service/licensemanager",
83+
"service/lightsail",
84+
"service/lookoutequipment",
85+
"service/machinelearning",
86+
"service/marketplaceagreement",
87+
"service/marketplacecommerceanalytics",
88+
"service/marketplaceentitlementservice",
89+
"service/marketplacemetering",
90+
"service/mediastore",
91+
"service/memorydb",
92+
"service/migrationhub",
93+
"service/migrationhubconfig",
94+
"service/mturk",
95+
"service/networkfirewall",
96+
"service/opensearchserverless",
97+
"service/opsworks",
98+
"service/opsworkscm",
99+
"service/organizations",
100+
"service/paymentcryptography",
101+
"service/personalize",
102+
"service/pi",
103+
"service/pinpointsmsvoicev2",
104+
"service/pricing",
105+
"service/proton",
106+
"service/qldbsession",
107+
"service/redshiftdata",
108+
"service/redshiftserverless",
109+
"service/rekognition",
110+
"service/resourcegroupstaggingapi",
111+
"service/route53domains",
112+
"service/route53recoverycluster",
113+
"service/route53resolver",
114+
"service/sagemaker",
115+
"service/secretsmanager",
116+
"service/servicecatalog",
117+
"service/servicediscovery",
118+
"service/servicequotas",
119+
"service/sfn",
120+
"service/shield",
121+
"service/sms",
122+
"service/snowball",
123+
"service/sqs",
124+
"service/ssm",
125+
"service/ssmcontacts",
126+
"service/ssoadmin",
127+
"service/storagegateway",
128+
"service/support",
129+
"service/swf",
130+
"service/textract",
131+
"service/timestreamquery",
132+
"service/timestreamwrite",
133+
"service/transcribe",
134+
"service/transfer",
135+
"service/translate",
136+
"service/verifiedpermissions",
137+
"service/voiceid",
138+
"service/waf",
139+
"service/wafregional",
140+
"service/wafv2",
141+
"service/workmail",
142+
"service/workspaces"
143+
]
144+
}

codegen/smithy-aws-go-codegen/src/main/java/software/amazon/smithy/aws/go/codegen/AwsProtocolUtils.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -196,30 +196,6 @@ static void generateHttpProtocolTests(GenerationContext context) {
196196
).generateProtocolTests();
197197
}
198198

199-
public static void writeJsonErrorMessageCodeDeserializer(GenerationContext context) {
200-
GoWriter writer = context.getWriter().get();
201-
// The error code could be in the headers, even though for this protocol it should be in the body.
202-
writer.write("headerCode := response.Header.Get(\"X-Amzn-ErrorType\")");
203-
writer.write("if len(headerCode) != 0 { errorCode = restjson.SanitizeErrorCode(headerCode) }");
204-
writer.write("");
205-
206-
initializeJsonDecoder(writer, "errorBody");
207-
writer.addUseImports(AwsGoDependency.AWS_REST_JSON_PROTOCOL);
208-
// This will check various body locations for the error code and error message
209-
writer.write("jsonCode, message, err := restjson.GetErrorInfo(decoder)");
210-
handleDecodeError(writer);
211-
212-
writer.addUseImports(SmithyGoDependency.IO);
213-
// Reset the body in case it needs to be used for anything else.
214-
writer.write("errorBody.Seek(0, io.SeekStart)");
215-
216-
// Only set the values if something was found so that we keep the default values.
217-
// The header version of the error wins out over either of the body fields.
218-
writer.write("if len(headerCode) == 0 && len(jsonCode) != 0 { errorCode = restjson.SanitizeErrorCode(jsonCode) }");
219-
writer.write("if len(message) != 0 { errorMessage = message }");
220-
writer.write("");
221-
}
222-
223199
public static void initializeJsonDecoder(GoWriter writer, String bodyLocation) {
224200
// Use a ring buffer and tee reader to help in pinpointing any deserialization errors.
225201
writer.addUseImports(SmithyGoDependency.SMITHY_IO);

codegen/smithy-aws-go-codegen/src/main/java/software/amazon/smithy/aws/go/codegen/JsonRpcProtocolGenerator.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.handleDecodeError;
1919
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.initializeJsonDecoder;
20-
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.writeJsonErrorMessageCodeDeserializer;
20+
import static software.amazon.smithy.go.codegen.GoWriter.goTemplate;
2121

2222
import java.util.HashSet;
2323
import java.util.Set;
@@ -44,6 +44,7 @@
4444
import software.amazon.smithy.model.traits.EventPayloadTrait;
4545
import software.amazon.smithy.go.codegen.endpoints.EndpointResolutionGenerator;
4646
import software.amazon.smithy.go.codegen.endpoints.FnGenerator;
47+
import software.amazon.smithy.utils.MapUtils;
4748

4849
/**
4950
* Handles generating the aws.rest-json protocol for services.
@@ -174,7 +175,30 @@ public void generateProtocolTests(GenerationContext context) {
174175

175176
@Override
176177
protected void writeErrorMessageCodeDeserializer(GenerationContext context) {
177-
writeJsonErrorMessageCodeDeserializer(context);
178+
var tmpl = goTemplate("""
179+
headerCode := response.Header.Get("X-Amzn-ErrorType")
180+
181+
$initDecoder:W
182+
bodyInfo, err := getProtocolErrorInfo(decoder)
183+
$handleDecodeError:W
184+
185+
errorBody.Seek(0, io.SeekStart)
186+
if typ, ok := resolveProtocolErrorType(headerCode, bodyInfo); ok {
187+
errorCode = restjson.SanitizeErrorCode(typ)
188+
}
189+
if len(bodyInfo.Message) != 0 {
190+
errorMessage = bodyInfo.Message
191+
}
192+
193+
""",
194+
MapUtils.of(
195+
"initDecoder", (GoWriter.Writable) writer -> initializeJsonDecoder(writer, "errorBody"),
196+
"handleDecodeError", (GoWriter.Writable) AwsProtocolUtils::handleDecodeError
197+
));
198+
context.getWriter().get()
199+
.addUseImports(AwsGoDependency.AWS_REST_JSON_PROTOCOL)
200+
.addUseImports(SmithyGoDependency.IO)
201+
.write(tmpl);
178202
}
179203

180204
@Override
@@ -367,4 +391,47 @@ public void generateEndpointResolution(GenerationContext context) {
367391
generator.generate(context);
368392
}
369393

394+
@Override
395+
public void generateSharedDeserializerComponents(GenerationContext context) {
396+
super.generateSharedDeserializerComponents(context);
397+
writeGetProtocolErrorInfo(context);
398+
}
399+
400+
private void writeGetProtocolErrorInfo(GenerationContext context) {
401+
var tmpl = goTemplate("""
402+
type protocolErrorInfo struct {
403+
Type string `json:"__type"`
404+
Message string
405+
Code any // nonstandard for awsjson but some services do present the type here
406+
}
407+
408+
func getProtocolErrorInfo(decoder *json.Decoder) (protocolErrorInfo, error) {
409+
var errInfo protocolErrorInfo
410+
if err := decoder.Decode(&errInfo); err != nil {
411+
if err == io.EOF {
412+
return errInfo, nil
413+
}
414+
return errInfo, err
415+
}
416+
417+
return errInfo, nil
418+
}
419+
420+
func resolveProtocolErrorType(headerType string, bodyInfo protocolErrorInfo) (string, bool) {
421+
if len(headerType) != 0 {
422+
return headerType, true
423+
} else if len(bodyInfo.Type) != 0 {
424+
return bodyInfo.Type, true
425+
} else if code, ok := bodyInfo.Code.(string); ok && len(code) != 0 {
426+
return code, true
427+
}
428+
return "", false
429+
}
430+
431+
""");
432+
context.getWriter().get()
433+
.addUseImports(SmithyGoDependency.JSON)
434+
.addUseImports(SmithyGoDependency.IO)
435+
.write(tmpl);
436+
}
370437
}

codegen/smithy-aws-go-codegen/src/main/java/software/amazon/smithy/aws/go/codegen/RestJsonProtocolGenerator.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import static software.amazon.smithy.go.codegen.integration.HttpProtocolGeneratorUtils.isShapeWithResponseBindings;
1919
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.handleDecodeError;
2020
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.initializeJsonDecoder;
21-
import static software.amazon.smithy.aws.go.codegen.AwsProtocolUtils.writeJsonErrorMessageCodeDeserializer;
2221

2322
import java.util.HashSet;
2423
import java.util.Optional;
@@ -363,7 +362,27 @@ protected void generateOperationDocumentDeserializer(
363362

364363
@Override
365364
protected void writeErrorMessageCodeDeserializer(GenerationContext context) {
366-
writeJsonErrorMessageCodeDeserializer(context);
365+
GoWriter writer = context.getWriter().get();
366+
// The error code could be in the headers, even though for this protocol it should be in the body.
367+
writer.write("headerCode := response.Header.Get(\"X-Amzn-ErrorType\")");
368+
writer.write("if len(headerCode) != 0 { errorCode = restjson.SanitizeErrorCode(headerCode) }");
369+
writer.write("");
370+
371+
initializeJsonDecoder(writer, "errorBody");
372+
writer.addUseImports(AwsGoDependency.AWS_REST_JSON_PROTOCOL);
373+
// This will check various body locations for the error code and error message
374+
writer.write("jsonCode, message, err := restjson.GetErrorInfo(decoder)");
375+
handleDecodeError(writer);
376+
377+
writer.addUseImports(SmithyGoDependency.IO);
378+
// Reset the body in case it needs to be used for anything else.
379+
writer.write("errorBody.Seek(0, io.SeekStart)");
380+
381+
// Only set the values if something was found so that we keep the default values.
382+
// The header version of the error wins out over either of the body fields.
383+
writer.write("if len(headerCode) == 0 && len(jsonCode) != 0 { errorCode = restjson.SanitizeErrorCode(jsonCode) }");
384+
writer.write("if len(message) != 0 { errorMessage = message }");
385+
writer.write("");
367386
}
368387

369388
@Override

0 commit comments

Comments
 (0)