Unverified Commit 4c852b1d authored by Zelda Hessler's avatar Zelda Hessler Committed by GitHub
Browse files

Add operation metadata to property bag just before sending request through middleware (#1920)

* update: add operation metadata to property bag during `make_operation`
add: test ensuring metadata is added to property bag
add: CHANGELOG.next.toml entry

* update: use new strategy for op metadata insertion
update: a new strategy requires a new test
update: CHANGELOG.next.toml

* format: run cargo fmt
parent c5c87be5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -52,3 +52,12 @@ message = "Fix bug that can cause panics in paginators"
references = ["smithy-rs#1903", "smithy-rs#1902"]
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"}
author = "rcoh"

[[smithy-rs]]
message = """
Operation metadata is now added to the property bag before sending requests allowing middlewares to behave
differently depending on the operation being sent.
"""
references = ["smithy-rs#1919"]
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"}
author = "Velfi"
+38 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ use aws_http::retry::AwsResponseRetryClassifier;
use aws_http::user_agent::AwsUserAgent;
use aws_inlineable::middleware::DefaultMiddleware;
use aws_sig_auth::signer::OperationSigningConfig;
use aws_smithy_client::erase::DynConnector;

use aws_smithy_client::test_connection::TestConnection;
use aws_smithy_http::body::SdkBody;
@@ -112,6 +113,7 @@ fn test_operation() -> Operation<TestOperationParser, AwsResponseRetryClassifier
    .unwrap();
    Operation::new(req, TestOperationParser)
        .with_retry_classifier(AwsResponseRetryClassifier::new())
        .with_metadata(operation::Metadata::new("test-op", "test-service"))
}

#[cfg(any(feature = "native-tls", feature = "rustls"))]
@@ -148,3 +150,39 @@ async fn e2e_test() {

    conn.assert_requests_match(&[]);
}

#[tokio::test]
async fn test_operation_metadata_is_available_to_middlewares() {
    let conn = TestConnection::new(vec![(
        http::Request::builder()
            .header(USER_AGENT, "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0")
            .header("x-amz-user-agent", "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0")
            .header(AUTHORIZATION, "AWS4-HMAC-SHA256 Credential=access_key/20210215/test-region/test-service-signing/aws4_request, SignedHeaders=host;x-amz-date;x-amz-user-agent, Signature=da249491d7fe3da22c2e09cbf910f37aa5b079a3cedceff8403d0b18a7bfab75")
            .header("x-amz-date", "20210215T184017Z")
            .uri(Uri::from_static("https://test-service.test-region.amazonaws.com/"))
            .body(SdkBody::from("request body")).unwrap(),
        http::Response::builder()
            .status(200)
            .body("response body")
            .unwrap(),
    )]);
    let client = aws_smithy_client::Client::builder()
        .middleware_fn(|req| {
            let metadata = req
                .properties()
                .get::<operation::Metadata>()
                .cloned()
                .unwrap();

            assert_eq!("test-op", metadata.name());
            assert_eq!("test-service", metadata.service());

            req
        })
        .connector(DynConnector::new(conn))
        .build();

    let resp = client.call(test_operation()).await;
    let resp = resp.expect("successful operation");
    assert_eq!(resp, "Hello!");
}
+6 −3
Original line number Diff line number Diff line
@@ -71,7 +71,8 @@ open class MakeOperationGenerator(
    ) {
        val operationName = symbolProvider.toSymbol(shape).name
        val baseReturnType = buildOperationType(implBlockWriter, shape, customizations)
        val returnType = "std::result::Result<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>"
        val returnType =
            "std::result::Result<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>"
        val outputSymbol = symbolProvider.toSymbol(shape)

        val takesOwnership = bodyGenerator.payloadMetadata(shape).takesOwnership
@@ -82,8 +83,10 @@ open class MakeOperationGenerator(

        implBlockWriter.docs("Consumes the builder and constructs an Operation<#D>", outputSymbol)
        Attribute.AllowUnusedMut.render(implBlockWriter) // For codegen simplicity
        Attribute.Custom("allow(clippy::let_and_return)").render(implBlockWriter) // For codegen simplicity, allow `let x = ...; x`
        Attribute.Custom("allow(clippy::needless_borrow)").render(implBlockWriter) // Allows builders that don’t consume the input borrow
        Attribute.Custom("allow(clippy::let_and_return)")
            .render(implBlockWriter) // For codegen simplicity, allow `let x = ...; x`
        Attribute.Custom("allow(clippy::needless_borrow)")
            .render(implBlockWriter) // Allows builders that don’t consume the input borrow
        implBlockWriter.rustBlockTemplate(
            "$fnType $functionName($self, _config: &#{config}::Config) -> $returnType",
            *codegenScope,
+2 −1
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ where
    }

    fn call(&mut self, req: Operation<ResponseHandler, RetryPolicy>) -> Self::Future {
        let (req, parts) = req.into_request_response();
        let (mut req, parts) = req.into_request_response();
        let handler = parts.response_handler;
        // send_operation records the full request-response lifecycle.
        // NOTE: For operations that stream output, only the setup is captured in this span.
@@ -103,6 +103,7 @@ where
        if let Some(metadata) = parts.metadata {
            span.record("operation", &metadata.name());
            span.record("service", &metadata.service());
            req.properties_mut().insert(metadata);
        }
        let resp = self.inner.call(req);
        let fut = async move {