Skip to content

Commit 50f8724

Browse files
authored
Merge branch 'main' into aws-smithy-mocks/avoid-experimental-stmt_expr_attributes
2 parents 822f9ad + adc0a46 commit 50f8724

112 files changed

Lines changed: 34251 additions & 9636 deletions

File tree

Some content is hidden

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

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ target/
6161
# python
6262
__pycache__
6363

64-
65-
# AI ccontext
64+
# AI context dirs
6665
.kiro/
67-
# Convenient naming convention I have been using to manage context between different workflows
6866
.kiro-*/

AGENTS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ val mimeType = RuntimeType.forInlineFun("APPLICATION_JSON", module) {
137137

138138
⚠️ **Footgun**: Name collisions mean only one implementation gets generated.
139139

140+
## Git Workflow
141+
142+
**Committing with pre-commit hooks:**
143+
144+
This repo uses pre-commit hooks (formatting, linting, `runtime-versioner`). Some hooks modify files (e.g., reformatting). When a hook modifies files, the commit will fail. To fix:
145+
146+
```bash
147+
git add -u
148+
git commit -m "your message"
149+
```
150+
151+
The `git add -u` stages the hook's modifications, and the second commit attempt will pass. Do **not** use `--no-verify` to skip hooks.
152+
140153
## GitHub CLI Integration
141154

142155
**View issues and PRs:**

aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ import software.amazon.smithy.model.shapes.MemberShape
1313
import software.amazon.smithy.model.shapes.OperationShape
1414
import software.amazon.smithy.model.shapes.ServiceShape
1515
import software.amazon.smithy.model.shapes.ShapeId
16+
import software.amazon.smithy.model.shapes.StructureShape
1617
import software.amazon.smithy.model.shapes.ToShapeId
1718
import software.amazon.smithy.model.traits.HttpQueryTrait
1819
import software.amazon.smithy.model.traits.HttpTrait
1920
import software.amazon.smithy.model.transform.ModelTransformer
2021
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
2122
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings
2223
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
24+
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
25+
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
2326
import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationSection
2427
import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization
2528
import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection
@@ -41,6 +44,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
4144
import software.amazon.smithy.rust.codegen.core.smithy.contextName
4245
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
4346
import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization
47+
import software.amazon.smithy.rust.codegen.core.smithy.generators.SchemaGenerator
4448
import software.amazon.smithy.rust.codegen.core.util.cloneOperation
4549
import software.amazon.smithy.rust.codegen.core.util.expectTrait
4650
import software.amazon.smithy.rust.codegen.core.util.thenSingletonListOf
@@ -141,6 +145,24 @@ class AwsPresigningDecorator internal constructor(
141145
TopDownIndex.of(ctx.model).getContainedOperations(ctx.serviceShape)
142146
.any { presignableOperations.containsKey(it.id) }
143147

148+
override fun operationCustomizations(
149+
codegenContext: ClientCodegenContext,
150+
operation: OperationShape,
151+
baseCustomizations: List<OperationCustomization>,
152+
): List<OperationCustomization> {
153+
val presignableOp = presignableOperations[operation.id]
154+
return if (presignableOp != null && presignableOp.hasModelTransforms()) {
155+
val transformedOp =
156+
codegenContext.model.expectShape(
157+
syntheticShapeId(operation.id),
158+
OperationShape::class.java,
159+
)
160+
baseCustomizations + PresignedSchemaCustomization(codegenContext, transformedOp)
161+
} else {
162+
baseCustomizations
163+
}
164+
}
165+
144166
override fun extraSections(codegenContext: ClientCodegenContext): List<AdHocCustomization> =
145167
anyPresignedShapes(codegenContext).thenSingletonListOf {
146168
adhocCustomization<CustomizableOperationSection.CustomizableOperationImpl> {
@@ -390,6 +412,43 @@ class AwsPresignedFluentBuilderMethod(
390412
}
391413
}
392414

415+
/**
416+
* Generates a `PRESIGNED_INPUT_SCHEMA` constant for operations with presigning model transforms.
417+
*/
418+
private class PresignedSchemaCustomization(
419+
private val codegenContext: ClientCodegenContext,
420+
private val transformedOperationShape: OperationShape,
421+
) : OperationCustomization() {
422+
override fun section(section: OperationSection): Writable =
423+
writable {
424+
when (section) {
425+
is OperationSection.AdditionalItems -> {
426+
val transformedInputShape =
427+
codegenContext.model.expectShape(
428+
transformedOperationShape.inputShape,
429+
StructureShape::class.java,
430+
)
431+
SchemaGenerator(
432+
codegenContext,
433+
this,
434+
transformedInputShape,
435+
schemaPrefix = "PRESIGNED",
436+
).renderSchemaOnly()
437+
}
438+
is OperationSection.OperationImplBlock -> {
439+
rustTemplate(
440+
"""
441+
/// The schema for this operation's presigned input shape.
442+
pub const PRESIGNED_INPUT_SCHEMA: &'static #{Schema} = &PRESIGNED_SCHEMA;
443+
""",
444+
"Schema" to RuntimeType.smithySchema(codegenContext.runtimeConfig).resolve("Schema"),
445+
)
446+
}
447+
else -> {}
448+
}
449+
}
450+
}
451+
393452
interface PresignModelTransform {
394453
fun transform(model: Model): Model
395454
}

aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package software.amazon.smithy.rustsdk
77

88
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
9+
import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency
910
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
1011
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
1112
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeCrateLocation
@@ -50,6 +51,7 @@ object AwsRuntimeType {
5051
visibility = Visibility.PUBCRATE,
5152
AwsCargoDependency.awsSigv4(runtimeConfig),
5253
CargoDependency.smithyRuntimeApiClient(runtimeConfig),
54+
InlineDependency.serializationSettings(runtimeConfig),
5355
),
5456
)
5557

aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre
2525
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
2626
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization
2727
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection
28+
import software.amazon.smithy.rust.codegen.core.smithy.generators.SchemaStructureCustomization
2829
import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization
2930
import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection
31+
import software.amazon.smithy.rust.codegen.core.smithy.generators.SyntheticSchemaMember
3032
import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization
3133
import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplSection
3234
import software.amazon.smithy.rust.codegen.core.smithy.isOptional
@@ -63,7 +65,25 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator {
6365
override fun structureCustomizations(
6466
codegenContext: ClientCodegenContext,
6567
baseCustomizations: List<StructureCustomization>,
66-
): List<StructureCustomization> = baseCustomizations + listOf(RequestIdStructureCustomization(codegenContext))
68+
): List<StructureCustomization> {
69+
// Replace SchemaStructureCustomization with one that includes the synthetic request ID member
70+
val syntheticMember =
71+
SyntheticSchemaMember(
72+
fieldName = "_$fieldName",
73+
schemaMemberName = fieldName,
74+
shapeType = "String",
75+
httpHeaderName = "x-amzn-requestid",
76+
)
77+
val updated =
78+
baseCustomizations.map { c ->
79+
if (c is SchemaStructureCustomization) {
80+
SchemaStructureCustomization(codegenContext, listOf(syntheticMember))
81+
} else {
82+
c
83+
}
84+
}
85+
return updated + listOf(RequestIdStructureCustomization(codegenContext))
86+
}
6787

6888
override fun builderCustomizations(
6989
codegenContext: ClientCodegenContext,

aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk
77

88
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
99
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
10+
import software.amazon.smithy.rust.codegen.client.smithy.customizations.SchemaSerdeAllowlist
1011
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
1112
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
1213
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
@@ -110,6 +111,19 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator {
110111
${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source());
111112
${section.serviceConfigBuilder}.set_behavior_version(${section.sdkConfig}.behavior_version());
112113
${section.serviceConfigBuilder}.set_auth_scheme_preference(${section.sdkConfig}.auth_scheme_preference().cloned());
114+
""",
115+
)
116+
if (SchemaSerdeAllowlist.usesSchemaSerdeExclusively(codegenContext)) {
117+
rust(
118+
"""
119+
if let Some(protocol) = ${section.sdkConfig}.protocol() {
120+
${section.serviceConfigBuilder}.set_protocol(Some(protocol.clone()));
121+
}
122+
""",
123+
)
124+
}
125+
rust(
126+
"""
113127
// setting `None` here removes the default
114128
if let Some(config) = ${section.sdkConfig}.stalled_stream_protection() {
115129
${section.serviceConfigBuilder}.set_stalled_stream_protection(Some(config));
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rustsdk
7+
8+
import org.junit.jupiter.api.Test
9+
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
10+
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
11+
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
12+
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
13+
import software.amazon.smithy.rust.codegen.core.testutil.integrationTest
14+
import software.amazon.smithy.rust.codegen.core.testutil.tokioTest
15+
16+
/**
17+
* Regression test for the schema-serde event-stream response deserializer
18+
* populating `_request_id` from the `x-amzn-requestid` response header.
19+
*
20+
* restJson1 is on [SchemaSerdeAllowlist] (see
21+
* `codegen-client/.../customizations/SchemaDecorator.kt`), so the generated
22+
* client hits `deserializeStreamingEventStreamSchema` in
23+
* [ResponseDeserializerGenerator] rather than the legacy streaming path.
24+
*
25+
* Before the fix (PR #4628), the schema-serde event-stream path skipped the
26+
* `MutateOutput` customization emitted by [BaseRequestIdDecorator], so
27+
* `output.request_id()` always returned `None` even when the service set
28+
* `x-amzn-requestid`.
29+
*/
30+
class EventStreamRequestIdTest {
31+
companion object {
32+
// `$version` is escaped to avoid Kotlin interpolation in a raw-string.
33+
private const val PREFIX = "\$version: \"2\""
34+
val model =
35+
"""
36+
$PREFIX
37+
namespace test
38+
39+
use aws.api#service
40+
use aws.auth#sigv4
41+
use aws.protocols#restJson1
42+
use smithy.rules#endpointRuleSet
43+
44+
@service(sdkId: "dontcare")
45+
@restJson1
46+
@sigv4(name: "dontcare")
47+
@auth([sigv4])
48+
@endpointRuleSet({
49+
"version": "1.0",
50+
"rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }],
51+
"parameters": {
52+
"Region": { "required": false, "type": "String", "builtIn": "AWS::Region" },
53+
}
54+
})
55+
service TestService {
56+
version: "2023-01-01",
57+
operations: [StreamOperation]
58+
}
59+
60+
@http(uri: "/StreamOperation", method: "POST")
61+
@optionalAuth
62+
operation StreamOperation {
63+
input: StreamInput,
64+
output: StreamOutput,
65+
}
66+
67+
@input
68+
structure StreamInput {
69+
@httpPayload
70+
body: StreamBody,
71+
}
72+
73+
@output
74+
structure StreamOutput {
75+
@httpPayload
76+
events: TestStream,
77+
}
78+
79+
@streaming
80+
union TestStream {
81+
hello: HelloEvent,
82+
}
83+
84+
// Unused by this test but required to make the input valid — every
85+
// `@streaming` union must appear in an operation input so the
86+
// marshaller generator runs.
87+
@streaming
88+
union StreamBody {
89+
ping: PingEvent,
90+
}
91+
92+
structure HelloEvent {
93+
@eventPayload
94+
data: String,
95+
}
96+
97+
structure PingEvent {}
98+
""".asSmithyModel()
99+
}
100+
101+
@Test
102+
fun `request_id is populated on event stream responses`() {
103+
awsSdkIntegrationTest(model) { context, rustCrate ->
104+
rustCrate.integrationTest("event_stream_request_id") {
105+
tokioTest("request_id_is_set_from_x_amzn_requestid_header") {
106+
val rc = context.runtimeConfig
107+
val moduleName = context.moduleUseName()
108+
rustTemplate(
109+
"""
110+
use $moduleName::config::{Credentials, Region};
111+
use $moduleName::{Config, Client};
112+
// Re-exported by BaseRequestIdDecorator.extras.
113+
use $moduleName::operation::RequestId;
114+
115+
// An empty event-stream body is a valid zero-frame stream.
116+
// The deserializer still constructs the output and sets
117+
// headers before wrapping the body in an EventReceiver.
118+
let http_client = #{StaticReplayClient}::new(vec![
119+
#{ReplayEvent}::new(
120+
#{HttpRequest1x}::builder()
121+
.uri("https://example.com/StreamOperation")
122+
.body(#{SdkBody}::empty())
123+
.unwrap(),
124+
#{HttpResponse1x}::builder()
125+
.status(200)
126+
.header("x-amzn-requestid", "test-request-id-1234")
127+
.header("content-type", "application/vnd.amazon.eventstream")
128+
.body(#{SdkBody}::empty())
129+
.unwrap(),
130+
),
131+
]);
132+
133+
let config = Config::builder()
134+
.credentials_provider(Credentials::for_tests())
135+
.region(Region::new("us-east-1"))
136+
.http_client(http_client.clone())
137+
.build();
138+
let client = Client::from_conf(config);
139+
140+
// We don't need to send any input events for this test —
141+
// the deserializer produces the output as soon as headers
142+
// arrive. We pass an empty stream for the `body` input.
143+
let input_stream = #{futures_util}::stream::empty();
144+
let output = client
145+
.stream_operation()
146+
.body(input_stream.into())
147+
.send()
148+
.await
149+
.expect("request should succeed");
150+
151+
assert_eq!(
152+
Some("test-request-id-1234"),
153+
output.request_id(),
154+
"request_id should be populated from x-amzn-requestid header",
155+
);
156+
""",
157+
"HttpRequest1x" to RuntimeType.HttpRequest1x,
158+
"HttpResponse1x" to RuntimeType.HttpResponse1x,
159+
"ReplayEvent" to
160+
RuntimeType.smithyHttpClientTestUtil(rc)
161+
.resolve("test_util::ReplayEvent"),
162+
"SdkBody" to RuntimeType.sdkBody(rc),
163+
"StaticReplayClient" to
164+
RuntimeType.smithyHttpClientTestUtil(rc)
165+
.resolve("test_util::StaticReplayClient"),
166+
"futures_util" to CargoDependency.FuturesUtil.toType(),
167+
)
168+
}
169+
}
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)