Skip to content

Commit 7801f44

Browse files
committed
Update generation to release this in waves
1 parent 76e240e commit 7801f44

1 file changed

Lines changed: 165 additions & 17 deletions

File tree

  • codegen/smithy-aws-go-codegen/src/main/java/software/amazon/smithy/aws/go/codegen/customization/service/s3

codegen/smithy-aws-go-codegen/src/main/java/software/amazon/smithy/aws/go/codegen/customization/service/s3/S3ErrorWith200Status.java

Lines changed: 165 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,19 @@
1515

1616
package software.amazon.smithy.aws.go.codegen.customization.service.s3;
1717

18+
import java.util.HashSet;
1819
import java.util.List;
1920
import java.util.Optional;
21+
import java.util.Set;
2022

2123
import software.amazon.smithy.aws.go.codegen.customization.AwsCustomGoDependency;
2224
import software.amazon.smithy.go.codegen.SymbolUtils;
2325
import software.amazon.smithy.go.codegen.integration.GoIntegration;
2426
import software.amazon.smithy.go.codegen.integration.MiddlewareRegistrar;
2527
import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin;
2628
import software.amazon.smithy.model.Model;
27-
import software.amazon.smithy.model.shapes.MemberShape;
2829
import software.amazon.smithy.model.shapes.OperationShape;
2930
import software.amazon.smithy.model.shapes.ServiceShape;
30-
import software.amazon.smithy.model.shapes.Shape;
3131
import software.amazon.smithy.model.shapes.ShapeId;
3232
import software.amazon.smithy.model.shapes.StructureShape;
3333
import software.amazon.smithy.model.traits.HttpPayloadTrait;
@@ -41,9 +41,153 @@
4141
* streaming binary payload or event-stream. In Smithy terms, this means all
4242
* S3 operations whose output does not contain a member with @httpPayload
4343
* targeting a @streaming blob, @streaming union, or string.
44+
*
45+
* Coverage is being rolled out in waves gated by call volume. Once all waves
46+
* have soaked, remove the wave sets and the isInEnabledWave check — the
47+
* model-driven logic in supports200Error is the correct final behavior.
4448
*/
4549
public class S3ErrorWith200Status implements GoIntegration {
46-
private static String ADD_ERROR_HANDLER_INTERNAL = "HandleResponseErrorWith200Status";
50+
private static final String ADD_ERROR_HANDLER_INTERNAL = "HandleResponseErrorWith200Status";
51+
52+
// Already covered on main — always enabled.
53+
private static final Set<String> ORIGINAL_OPERATIONS = Set.of(
54+
"CopyObject", "UploadPartCopy", "CompleteMultipartUpload"
55+
);
56+
57+
// Wave 1: < 1B req/week. Very low risk.
58+
private static final Set<String> WAVE_1_OPERATIONS = Set.of(
59+
"AbortMultipartUpload",
60+
"CreateBucketMetadataConfiguration",
61+
"CreateBucketMetadataTableConfiguration",
62+
"CreateSession",
63+
"DeleteBucket",
64+
"DeleteBucketAnalyticsConfiguration",
65+
"DeleteBucketCors",
66+
"DeleteBucketEncryption",
67+
"DeleteBucketIntelligentTieringConfiguration",
68+
"DeleteBucketInventoryConfiguration",
69+
"DeleteBucketLifecycle",
70+
"DeleteBucketMetadataConfiguration",
71+
"DeleteBucketMetadataTableConfiguration",
72+
"DeleteBucketMetricsConfiguration",
73+
"DeleteBucketOwnershipControls",
74+
"DeleteBucketPolicy",
75+
"DeleteBucketReplication",
76+
"DeleteBucketTagging",
77+
"DeleteBucketWebsite",
78+
"DeleteObjectAnnotation",
79+
"DeleteObjectTagging",
80+
"DeletePublicAccessBlock",
81+
"GetBucketAbac",
82+
"GetBucketAnalyticsConfiguration",
83+
"GetBucketIntelligentTieringConfiguration",
84+
"GetBucketInventoryConfiguration",
85+
"GetBucketMetadataConfiguration",
86+
"GetBucketMetadataTableConfiguration",
87+
"GetBucketMetricsConfiguration",
88+
"GetBucketTagging",
89+
"GetObjectAcl",
90+
"GetObjectAttributes",
91+
"GetObjectLegalHold",
92+
"GetObjectRetention",
93+
"GetPublicAccessBlock",
94+
"ListBucketAnalyticsConfigurations",
95+
"ListBucketIntelligentTieringConfigurations",
96+
"ListBucketInventoryConfigurations",
97+
"ListBucketMetricsConfigurations",
98+
"ListMultipartUploads",
99+
"ListObjectAnnotations",
100+
"ListParts",
101+
"PutBucketAbac",
102+
"PutBucketAccelerateConfiguration",
103+
"PutBucketCors",
104+
"PutBucketIntelligentTieringConfiguration",
105+
"PutBucketInventoryConfiguration",
106+
"PutBucketLifecycleConfiguration",
107+
"PutBucketLogging",
108+
"PutBucketMetricsConfiguration",
109+
"PutBucketNotificationConfiguration",
110+
"PutBucketOwnershipControls",
111+
"PutBucketReplication",
112+
"PutBucketRequestPayment",
113+
"PutBucketTagging",
114+
"PutBucketVersioning",
115+
"PutBucketWebsite",
116+
"PutObjectAnnotation",
117+
"PutObjectLegalHold",
118+
"PutObjectLockConfiguration",
119+
"PutPublicAccessBlock",
120+
"RenameObject",
121+
"RestoreObject",
122+
"UpdateBucketMetadataAnnotationTableConfiguration",
123+
"UpdateBucketMetadataInventoryTableConfiguration",
124+
"UpdateBucketMetadataJournalTableConfiguration",
125+
"UpdateObjectEncryption",
126+
"WriteGetObjectResponse"
127+
);
128+
129+
// Wave 2: 600M–5B req/week. Low-medium risk.
130+
private static final Set<String> WAVE_2_OPERATIONS = Set.of(
131+
"GetBucketAccelerateConfiguration",
132+
"GetBucketCors",
133+
"GetBucketLifecycleConfiguration",
134+
"GetBucketLogging",
135+
"GetBucketNotificationConfiguration",
136+
"GetBucketOwnershipControls",
137+
"GetBucketPolicyStatus",
138+
"GetBucketReplication",
139+
"GetBucketRequestPayment",
140+
"GetBucketVersioning",
141+
"GetBucketWebsite",
142+
"GetObjectLockConfiguration",
143+
"GetPublicAccessBlock",
144+
"ListBuckets",
145+
"ListDirectoryBuckets",
146+
"PutBucketAcl",
147+
"PutBucketEncryption",
148+
"PutBucketPolicy"
149+
);
150+
151+
// Wave 3: 5B–20B req/week. Medium risk.
152+
private static final Set<String> WAVE_3_OPERATIONS = Set.of(
153+
"CreateMultipartUpload",
154+
"DeleteObjects",
155+
"GetBucketAcl",
156+
"GetBucketEncryption",
157+
"GetBucketLocation",
158+
"GetObjectTagging",
159+
"HeadBucket",
160+
"ListObjectVersions",
161+
"PutObjectRetention",
162+
"PutObjectTagging"
163+
);
164+
165+
// Wave 4: 50B+ req/week. Medium-high risk (volume).
166+
private static final Set<String> WAVE_4_OPERATIONS = Set.of(
167+
"CreateBucket",
168+
"DeleteObject",
169+
"HeadObject",
170+
"ListObjects",
171+
"ListObjectsV2",
172+
"PutObject",
173+
"UploadPart"
174+
);
175+
176+
/**
177+
* Combined set of all currently enabled operations. To enable a wave,
178+
* add it to this set. To finish rollout, delete all wave sets and the
179+
* isInEnabledWave check entirely.
180+
*/
181+
private static final Set<String> ENABLED_OPERATIONS = buildEnabledOperations();
182+
183+
private static Set<String> buildEnabledOperations() {
184+
Set<String> enabled = new HashSet<>(ORIGINAL_OPERATIONS);
185+
enabled.addAll(WAVE_1_OPERATIONS);
186+
// enabled.addAll(WAVE_2_OPERATIONS);
187+
// enabled.addAll(WAVE_3_OPERATIONS);
188+
// enabled.addAll(WAVE_4_OPERATIONS);
189+
return Set.copyOf(enabled);
190+
}
47191

48192
@Override
49193
public byte getOrder() {
@@ -68,29 +212,33 @@ public List<RuntimeClientPlugin> getClientPlugins() {
68212
/**
69213
* Returns true if the operation supports error response with 200 ok status code.
70214
*
71-
* Per internal specification, this applies to all S3 operations whose output
72-
* does NOT contain an @httpPayload member targeting either:
73-
* - a shape with the @streaming trait (blob or event stream)
74-
* - a string shape
215+
* Uses a two-layer check:
216+
* 1. Model-driven: excludes operations with @httpPayload targeting @streaming or string.
217+
* 2. Wave gate: only enables operations that are in a currently-active wave.
218+
*
219+
* Once all waves are shipped and baked, remove the wave gate (step 2) and
220+
* the model-driven logic alone becomes the final behavior.
75221
*/
76222
private static boolean supports200Error(Model model, ServiceShape service, OperationShape operation) {
77223
if (!isS3Service(model, service)) {
78224
return false;
79225
}
80226

227+
// Model-driven exclusion: this is the correct final logic.
81228
Optional<ShapeId> output = operation.getOutput();
82-
if (!output.isPresent()) {
83-
// No output structure means a structured (empty) XML response, apply the check.
84-
return true;
229+
if (output.isPresent()) {
230+
StructureShape outputShape = model.expectShape(output.get(), StructureShape.class);
231+
boolean excluded = outputShape.getAllMembers().values().stream()
232+
.filter(member -> member.hasTrait(HttpPayloadTrait.class))
233+
.map(member -> model.expectShape(member.getTarget()))
234+
.anyMatch(target -> target.hasTrait(StreamingTrait.class) || target.isStringShape());
235+
if (excluded) {
236+
return false;
237+
}
85238
}
86239

87-
StructureShape outputShape = model.expectShape(output.get(), StructureShape.class);
88-
89-
// Exclude if any @httpPayload member targets a @streaming shape or a string.
90-
return outputShape.getAllMembers().values().stream()
91-
.filter(member -> member.hasTrait(HttpPayloadTrait.class))
92-
.map(member -> model.expectShape(member.getTarget()))
93-
.noneMatch(target -> target.hasTrait(StreamingTrait.class) || target.isStringShape());
240+
// Wave gate: remove this check once all waves are shipped and baked.
241+
return ENABLED_OPERATIONS.contains(operation.getId().getName(service));
94242
}
95243

96244
// returns true if service is s3

0 commit comments

Comments
 (0)