Skip to content

Commit 30e6db0

Browse files
Merge branch 'main' into RFC30/test2
2 parents 8d78bd4 + ddba460 commit 30e6db0

File tree

101 files changed

+2461
-592
lines changed

Some content is hidden

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

101 files changed

+2461
-592
lines changed

CHANGELOG.next.toml

+38-2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ message = """The middleware system has been reworked as we push for a unified, s
210210
211211
- A `ServiceShape` trait has been added.
212212
- The `Plugin` trait has been simplified.
213+
- The `HttpMarker` and `ModelMarker` marker traits have been added to better distinguish when plugins run and what they have access to.
213214
- The `Operation` structure has been removed.
214215
- A `Scoped` `Plugin` has been added.
215216
@@ -371,6 +372,8 @@ where
371372
PrintService { inner, name: Op::ID.name() }
372373
}
373374
}
375+
376+
impl HttpMarker for PrintPlugin { }
374377
```
375378
376379
Alternatively, using the new `ServiceShape`, implemented on `Ser`:
@@ -397,6 +400,11 @@ let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plug
397400
.unwrap();
398401
```
399402
403+
To better distinguish when a plugin runs and what it has access to, `Plugin`s now have to additionally implement the `HttpMarker` marker trait, the `ModelMarker` marker trait, or both:
404+
405+
- A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized.
406+
- A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized.
407+
400408
The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally.
401409
402410
Because `Plugin` is now closer to `tower::Layer` we have two canonical converters:
@@ -413,6 +421,34 @@ let plugin = /* some plugin */;
413421
let layer = LayerPlugin::new::<SomeProtocol, SomeOperation>(plugin);
414422
```
415423
424+
## Removal of `PluginPipeline`
425+
426+
Since plugins now come in two flavors (those marked with `HttpMarker` and those marked with `ModelMarker`) that shouldn't be mixed in a collection of plugins, the primary way of concatenating plugins, `PluginPipeline` has been removed in favor of the `HttpPlugins` and `ModelPlugins` types, which eagerly check that whenever a plugin is pushed, it is of the expected type.
427+
428+
This worked before, but you wouldn't be able to do apply this collection of plugins anywhere; if you tried to, the compilation error messages would not be very helpful:
429+
430+
```rust
431+
use aws_smithy_http_server::plugin::PluginPipeline;
432+
433+
let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin);
434+
```
435+
436+
Now collections of plugins must contain plugins of the same flavor:
437+
438+
```rust
439+
use aws_smithy_http_server::plugin::{HttpPlugins, ModelPlugins};
440+
441+
let http_plugins = HttpPlugins::new()
442+
.push(http_plugin)
443+
// .push(model_plugin) // This fails to compile with a helpful error message.
444+
.push(&http_and_model_plugin);
445+
let model_plugins = ModelPlugins::new()
446+
.push(model_plugin)
447+
.push(&http_and_model_plugin);
448+
```
449+
450+
In the above example, `&http_and_model_plugin` implements both `HttpMarker` and `ModelMarker`, so we can add it to both collections.
451+
416452
## Removal of `Operation`
417453
418454
The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to
@@ -495,8 +531,8 @@ let scoped_plugin = Scoped::new::<SomeScope>(plugin);
495531
```
496532
497533
"""
498-
references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"]
499-
meta = { "breaking" = true, "tada" = false, "bug" = false }
534+
references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779", "smithy-rs#2827"]
535+
meta = { "breaking" = true, "tada" = true, "bug" = false }
500536
author = "hlbarber"
501537

502538
[[smithy-rs]]

aws/rust-runtime/aws-config/src/test_case.rs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub(crate) struct Metadata {
136136
name: String,
137137
}
138138

139+
// TODO(enableNewSmithyRuntimeCleanup): Replace Tee, capture_test_logs, and Rx with
140+
// the implementations added to aws_smithy_runtime::test_util::capture_test_logs
139141
struct Tee<W> {
140142
buf: Arc<Mutex<Vec<u8>>>,
141143
quiet: bool,

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ class AwsPresignedFluentBuilderMethod(
322322
323323
let runtime_plugins = #{Operation}::operation_runtime_plugins(
324324
self.handle.runtime_plugins.clone(),
325-
self.config_override
325+
&self.handle.conf,
326+
self.config_override,
326327
)
327328
.with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config, #{payload_override}))
328329
#{alternate_presigning_serializer_registration};

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

+30
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,36 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom
191191
}
192192
}
193193

194+
is ServiceConfig.OperationConfigOverride -> {
195+
rustTemplate(
196+
"""
197+
match (
198+
layer
199+
.load::<#{CredentialsCache}>()
200+
.cloned(),
201+
layer
202+
.load::<#{SharedCredentialsProvider}>()
203+
.cloned(),
204+
) {
205+
(#{None}, #{None}) => {}
206+
(#{None}, _) => {
207+
panic!("also specify `.credentials_cache` when overriding credentials provider for the operation");
208+
}
209+
(_, #{None}) => {
210+
panic!("also specify `.credentials_provider` when overriding credentials cache for the operation");
211+
}
212+
(
213+
#{Some}(credentials_cache),
214+
#{Some}(credentials_provider),
215+
) => {
216+
layer.store_put(credentials_cache.create_cache(credentials_provider));
217+
}
218+
}
219+
""",
220+
*codegenScope,
221+
)
222+
}
223+
194224
else -> emptySection
195225
}
196226
}

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

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
1515
import software.amazon.smithy.rust.codegen.core.rustlang.writable
1616
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
1717
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
18+
import software.amazon.smithy.rust.codegen.core.util.letIf
1819

1920
class RetryClassifierDecorator : ClientCodegenDecorator {
2021
override val name: String = "RetryPolicy"
@@ -25,10 +26,12 @@ class RetryClassifierDecorator : ClientCodegenDecorator {
2526
operation: OperationShape,
2627
baseCustomizations: List<OperationCustomization>,
2728
): List<OperationCustomization> =
28-
baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) + OperationRetryClassifiersFeature(
29-
codegenContext,
30-
operation,
31-
)
29+
(baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig)).letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) {
30+
it + OperationRetryClassifiersFeature(
31+
codegenContext,
32+
operation,
33+
)
34+
}
3235
}
3336

3437
class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() {

aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt

-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class TimestreamDecorator : ClientCodegenDecorator {
5252
Visibility.PUBLIC,
5353
CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")),
5454
)
55-
val runtimeMode = codegenContext.smithyRuntimeMode
5655
rustCrate.lib {
5756
// helper function to resolve an endpoint given a base client
5857
rustTemplate(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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.Attribute
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.testModule
14+
import software.amazon.smithy.rust.codegen.core.testutil.tokioTest
15+
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
16+
17+
internal class CredentialCacheConfigTest {
18+
private val model = """
19+
namespace com.example
20+
use aws.protocols#awsJson1_0
21+
use aws.api#service
22+
use smithy.rules#endpointRuleSet
23+
24+
@service(sdkId: "Some Value")
25+
@awsJson1_0
26+
@endpointRuleSet({
27+
"version": "1.0",
28+
"rules": [{
29+
"type": "endpoint",
30+
"conditions": [{"fn": "isSet", "argv": [{"ref": "Region"}]}],
31+
"endpoint": { "url": "https://example.com" }
32+
}],
33+
"parameters": {
34+
"Region": { "required": false, "type": "String", "builtIn": "AWS::Region" },
35+
}
36+
})
37+
service HelloService {
38+
operations: [SayHello],
39+
version: "1"
40+
}
41+
42+
@optionalAuth
43+
operation SayHello { input: TestInput }
44+
structure TestInput {
45+
foo: String,
46+
}
47+
""".asSmithyModel()
48+
49+
@Test
50+
fun `config override for credentials`() {
51+
awsSdkIntegrationTest(model, defaultToOrchestrator = true) { clientCodegenContext, rustCrate ->
52+
val runtimeConfig = clientCodegenContext.runtimeConfig
53+
val codegenScope = arrayOf(
54+
*RuntimeType.preludeScope,
55+
"Credentials" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig)
56+
.resolve("Credentials"),
57+
"CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig)
58+
.resolve("cache::CredentialsCache"),
59+
"ProvideCachedCredentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig)
60+
.resolve("cache::ProvideCachedCredentials"),
61+
"RuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig)
62+
.resolve("client::runtime_plugin::RuntimePlugin"),
63+
"SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig)
64+
.resolve("cache::SharedCredentialsCache"),
65+
"SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig)
66+
.resolve("provider::SharedCredentialsProvider"),
67+
)
68+
rustCrate.testModule {
69+
unitTest(
70+
"test_overriding_only_credentials_provider_should_panic",
71+
additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_cache` when overriding credentials provider for the operation")),
72+
) {
73+
rustTemplate(
74+
"""
75+
use #{RuntimePlugin};
76+
77+
let client_config = crate::config::Config::builder().build();
78+
let config_override =
79+
crate::config::Config::builder().credentials_provider(#{Credentials}::for_tests());
80+
let sut = crate::config::ConfigOverrideRuntimePlugin {
81+
client_config: client_config.config().unwrap(),
82+
config_override,
83+
};
84+
85+
// this should cause `panic!`
86+
let _ = sut.config().unwrap();
87+
""",
88+
*codegenScope,
89+
)
90+
}
91+
92+
unitTest(
93+
"test_overriding_only_credentials_cache_should_panic",
94+
additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_provider` when overriding credentials cache for the operation")),
95+
) {
96+
rustTemplate(
97+
"""
98+
use #{RuntimePlugin};
99+
100+
let client_config = crate::config::Config::builder().build();
101+
let config_override = crate::config::Config::builder()
102+
.credentials_cache(#{CredentialsCache}::no_caching());
103+
let sut = crate::config::ConfigOverrideRuntimePlugin {
104+
client_config: client_config.config().unwrap(),
105+
config_override,
106+
};
107+
108+
// this should cause `panic!`
109+
let _ = sut.config().unwrap();
110+
""",
111+
*codegenScope,
112+
)
113+
}
114+
115+
tokioTest("test_overriding_cache_and_provider_leads_to_shared_credentials_cache_in_layer") {
116+
rustTemplate(
117+
"""
118+
use #{ProvideCachedCredentials};
119+
use #{RuntimePlugin};
120+
121+
let client_config = crate::config::Config::builder()
122+
.credentials_provider(#{Credentials}::for_tests())
123+
.build();
124+
let client_config_layer = client_config.config().unwrap();
125+
126+
// make sure test credentials are set in the client config level
127+
assert_eq!(#{Credentials}::for_tests(),
128+
client_config_layer
129+
.load::<#{SharedCredentialsCache}>()
130+
.unwrap()
131+
.provide_cached_credentials()
132+
.await
133+
.unwrap()
134+
);
135+
136+
let credentials = #{Credentials}::new(
137+
"test",
138+
"test",
139+
#{None},
140+
#{None},
141+
"test",
142+
);
143+
let config_override = crate::config::Config::builder()
144+
.credentials_cache(#{CredentialsCache}::lazy())
145+
.credentials_provider(credentials.clone());
146+
let sut = crate::config::ConfigOverrideRuntimePlugin {
147+
client_config: client_config_layer,
148+
config_override,
149+
};
150+
let sut_layer = sut.config().unwrap();
151+
152+
// make sure `.provide_cached_credentials` returns credentials set through `config_override`
153+
assert_eq!(credentials,
154+
sut_layer
155+
.load::<#{SharedCredentialsCache}>()
156+
.unwrap()
157+
.provide_cached_credentials()
158+
.await
159+
.unwrap()
160+
);
161+
""",
162+
*codegenScope,
163+
)
164+
}
165+
166+
unitTest("test_not_overriding_cache_and_provider_leads_to_no_shared_credentials_cache_in_layer") {
167+
rustTemplate(
168+
"""
169+
use #{RuntimePlugin};
170+
171+
let client_config = crate::config::Config::builder().build();
172+
let config_override = crate::config::Config::builder();
173+
let sut = crate::config::ConfigOverrideRuntimePlugin {
174+
client_config: client_config.config().unwrap(),
175+
config_override,
176+
};
177+
let sut_layer = sut.config().unwrap();
178+
assert!(sut_layer
179+
.load::<#{SharedCredentialsCache}>()
180+
.is_none());
181+
""",
182+
*codegenScope,
183+
)
184+
}
185+
}
186+
}
187+
}
188+
}

aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk
77

88
import software.amazon.smithy.model.Model
99
import software.amazon.smithy.model.node.ObjectNode
10+
import software.amazon.smithy.model.node.StringNode
1011
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
1112
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings
1213
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
@@ -17,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
1718
import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams
1819
import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig
1920
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
21+
import software.amazon.smithy.rust.codegen.core.util.letIf
2022
import java.io.File
2123

2224
// In aws-sdk-codegen, the working dir when gradle runs tests is actually `./aws`. So, to find the smithy runtime, we need
@@ -35,8 +37,10 @@ fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? =
3537
settings = settings ?: testClientRustSettings(runtimeConfig = AwsTestRuntimeConfig),
3638
)
3739

40+
// TODO(enableNewSmithyRuntimeCleanup): Remove defaultToOrchestrator once the runtime switches to the orchestrator
3841
fun awsSdkIntegrationTest(
3942
model: Model,
43+
defaultToOrchestrator: Boolean = false,
4044
test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> },
4145
) =
4246
clientIntegrationTest(
@@ -58,6 +62,9 @@ fun awsSdkIntegrationTest(
5862
"codegen",
5963
ObjectNode.builder()
6064
.withMember("includeFluentClient", false)
65+
.letIf(defaultToOrchestrator) {
66+
it.withMember("enableNewSmithyRuntime", StringNode.from("orchestrator"))
67+
}
6168
.build(),
6269
).build(),
6370
),

0 commit comments

Comments
 (0)