Unverified Commit 22018ce1 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Fix the Route53 tests in the orchestrator impl (#2796)

This PR fixes the Route53 tests for the client orchestrator
implementation.

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent f97aa12d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@ pub mod glacier_interceptors;
/// Default middleware stack for AWS services
pub mod middleware;

/// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests
pub mod route53_resource_id_preprocessor_middleware;

/// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests
pub mod route53_resource_id_preprocessor;

+57 −2
Original line number Diff line number Diff line
@@ -3,10 +3,19 @@
 * SPDX-License-Identifier: Apache-2.0
 */

#![allow(dead_code)]

use aws_smithy_runtime_api::client::interceptors::{
    BeforeSerializationInterceptorContextMut, BoxError, Interceptor,
};
use aws_smithy_types::config_bag::ConfigBag;
use std::fmt;
use std::marker::PhantomData;

// This function is only used to strip prefixes from resource IDs at the time they're passed as
// input to a request. Resource IDs returned in responses may or may not include a prefix.
/// Strip the resource type prefix from resource ID return
pub fn trim_resource_id(resource_id: &mut Option<String>) {
fn trim_resource_id(resource_id: &mut Option<String>) {
    const PREFIXES: &[&str] = &[
        "/hostedzone/",
        "hostedzone/",
@@ -28,9 +37,55 @@ pub fn trim_resource_id(resource_id: &mut Option<String>) {
    }
}

pub(crate) struct Route53ResourceIdInterceptor<G, T>
where
    G: for<'a> Fn(&'a mut T) -> &'a mut Option<String>,
{
    get_mut_resource_id: G,
    _phantom: PhantomData<T>,
}

impl<G, T> fmt::Debug for Route53ResourceIdInterceptor<G, T>
where
    G: for<'a> Fn(&'a mut T) -> &'a mut Option<String>,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Route53ResourceIdInterceptor").finish()
    }
}

impl<G, T> Route53ResourceIdInterceptor<G, T>
where
    G: for<'a> Fn(&'a mut T) -> &'a mut Option<String>,
{
    pub(crate) fn new(get_mut_resource_id: G) -> Self {
        Self {
            get_mut_resource_id,
            _phantom: Default::default(),
        }
    }
}

impl<G, T> Interceptor for Route53ResourceIdInterceptor<G, T>
where
    G: for<'a> Fn(&'a mut T) -> &'a mut Option<String>,
    T: fmt::Debug + Send + Sync + 'static,
{
    fn modify_before_serialization(
        &self,
        context: &mut BeforeSerializationInterceptorContextMut<'_>,
        _cfg: &mut ConfigBag,
    ) -> Result<(), BoxError> {
        let input: &mut T = context.input_mut().downcast_mut().expect("correct type");
        let field = (self.get_mut_resource_id)(input);
        trim_resource_id(field);
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use crate::route53_resource_id_preprocessor::trim_resource_id;
    use super::trim_resource_id;

    #[test]
    fn does_not_change_regular_zones() {
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

// TODO(enableNewSmithyRuntimeCleanup): Delete this module

// This function is only used to strip prefixes from resource IDs at the time they're passed as
// input to a request. Resource IDs returned in responses may or may not include a prefix.
/// Strip the resource type prefix from resource ID return
pub fn trim_resource_id(resource_id: &mut Option<String>) {
    const PREFIXES: &[&str] = &[
        "/hostedzone/",
        "hostedzone/",
        "/change/",
        "change/",
        "/delegationset/",
        "delegationset/",
    ];

    for prefix in PREFIXES {
        if let Some(id) = resource_id
            .as_deref()
            .unwrap_or_default()
            .strip_prefix(prefix)
        {
            *resource_id = Some(id.to_string());
            return;
        }
    }
}

#[cfg(test)]
mod test {
    use crate::route53_resource_id_preprocessor_middleware::trim_resource_id;

    #[test]
    fn does_not_change_regular_zones() {
        struct OperationInput {
            resource: Option<String>,
        }

        let mut operation = OperationInput {
            resource: Some("Z0441723226OZ66S5ZCNZ".to_string()),
        };
        trim_resource_id(&mut operation.resource);
        assert_eq!(
            &operation.resource.unwrap_or_default(),
            "Z0441723226OZ66S5ZCNZ"
        );
    }

    #[test]
    fn sanitizes_prefixed_zone() {
        struct OperationInput {
            change_id: Option<String>,
        }

        let mut operation = OperationInput {
            change_id: Some("/change/Z0441723226OZ66S5ZCNZ".to_string()),
        };
        trim_resource_id(&mut operation.change_id);
        assert_eq!(
            &operation.change_id.unwrap_or_default(),
            "Z0441723226OZ66S5ZCNZ"
        );
    }

    #[test]
    fn allow_no_leading_slash() {
        struct OperationInput {
            hosted_zone: Option<String>,
        }

        let mut operation = OperationInput {
            hosted_zone: Some("hostedzone/Z0441723226OZ66S5ZCNZ".to_string()),
        };
        trim_resource_id(&mut operation.hosted_zone);
        assert_eq!(
            &operation.hosted_zone.unwrap_or_default(),
            "Z0441723226OZ66S5ZCNZ"
        );
    }
}
+44 −14
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.model.traits.HttpLabelTrait
import software.amazon.smithy.model.transform.ModelTransformer
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
@@ -45,10 +46,14 @@ class Route53Decorator : ClientCodegenDecorator {
        operation: OperationShape,
        baseCustomizations: List<OperationCustomization>,
    ): List<OperationCustomization> {
        val hostedZoneMember =
            operation.inputShape(codegenContext.model).members().find { it.hasTrait<TrimResourceId>() }
        val inputShape = operation.inputShape(codegenContext.model)
        val hostedZoneMember = inputShape.members().find { it.hasTrait<TrimResourceId>() }
        return if (hostedZoneMember != null) {
            baseCustomizations + TrimResourceIdCustomization(codegenContext.symbolProvider.toMemberName(hostedZoneMember))
            baseCustomizations + TrimResourceIdCustomization(
                codegenContext,
                inputShape,
                codegenContext.symbolProvider.toMemberName(hostedZoneMember),
            )
        } else {
            baseCustomizations
        }
@@ -59,25 +64,50 @@ class Route53Decorator : ClientCodegenDecorator {
    }
}

class TrimResourceIdCustomization(private val fieldName: String) : OperationCustomization() {
class TrimResourceIdCustomization(
    private val codegenContext: ClientCodegenContext,
    private val inputShape: StructureShape,
    private val fieldName: String,
) :
    OperationCustomization() {
    override fun mutSelf(): Boolean = true
    override fun consumesSelf(): Boolean = true

    private val trimResourceId =
    override fun section(section: OperationSection): Writable = writable {
        when (section) {
            // TODO(enableNewSmithyRuntimeCleanup): Delete this `MutateInput` section
            is OperationSection.MutateInput -> {
                val trimResourceId =
                    RuntimeType.forInlineDependency(
            InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"),
                        InlineAwsDependency.forRustFile("route53_resource_id_preprocessor_middleware"),
                    )
                        .resolve("trim_resource_id")

    override fun section(section: OperationSection): Writable {
        return when (section) {
            is OperationSection.MutateInput -> writable {
                rustTemplate(
                    "#{trim_resource_id}(&mut ${section.input}.$fieldName);",
                    "trim_resource_id" to trimResourceId,
                )
            }
            else -> emptySection

            is OperationSection.AdditionalInterceptors -> {
                section.registerInterceptor(codegenContext.runtimeConfig, this) {
                    val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig)
                    val interceptor =
                        RuntimeType.forInlineDependency(
                            InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"),
                        ).resolve("Route53ResourceIdInterceptor")
                    rustTemplate(
                        """
                        #{Route53ResourceIdInterceptor}::new(|input: &mut #{Input}| {
                            &mut input.$fieldName
                        })
                        """,
                        "Input" to codegenContext.symbolProvider.toSymbol(inputShape),
                        "Route53ResourceIdInterceptor" to interceptor,
                        "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"),
                    )
                }
            }
            else -> {}
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -21,14 +21,13 @@ services_that_dont_compile=(\
# TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made
services_that_compile=(\
    "aws-config"\
    "dynamodb"\
    "route53"\
    "s3"\
    "sts"\
)

services_that_pass_tests=(\
    "config"\
    "dynamodb"\
    "ec2"\
    "ecs"\
    "glacier"\
@@ -37,6 +36,7 @@ services_that_pass_tests=(\
    "lambda"\
    "polly"\
    "qldbsession"\
    "route53"\
    "s3control"\
    "sso"\
    "transcribestreaming"\