Unverified Commit cda6ed61 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Add regions to AWS config (#196)

* wip

* Regions in AWS Config

* Add region docs
parent 27e00dd0
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
use aws_types::{Region, SigningRegion, SigningService};
use http::Uri;
use smithy_http::endpoint::{Endpoint, EndpointPrefix};
use smithy_http::middleware::MapRequest;
use smithy_http::operation::Request;
use smithy_http::property_bag::PropertyBag;
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use std::sync::Arc;

use http::Uri;

use aws_types::region::{Region, SigningRegion};
use aws_types::SigningService;
use smithy_http::endpoint::{Endpoint, EndpointPrefix};
use smithy_http::middleware::MapRequest;
use smithy_http::operation::Request;
use smithy_http::property_bag::PropertyBag;

/// Endpoint to connect to an AWS Service
///
/// An `AwsEndpoint` captures all necessary information needed to connect to an AWS service, including:
@@ -164,13 +167,17 @@ impl MapRequest for AwsEndpointStage {

#[cfg(test)]
mod test {
    use crate::{set_endpoint_resolver, AwsEndpointStage, DefaultAwsEndpointResolver};
    use aws_types::{Region, SigningRegion, SigningService};
    use std::sync::Arc;

    use http::Uri;

    use aws_types::region::{Region, SigningRegion};
    use aws_types::SigningService;
    use smithy_http::body::SdkBody;
    use smithy_http::middleware::MapRequest;
    use smithy_http::operation;
    use std::sync::Arc;

    use crate::{AwsEndpointStage, DefaultAwsEndpointResolver, set_endpoint_resolver};

    #[test]
    fn default_endpoint_updates_request() {
+2 −40
Original line number Diff line number Diff line
use std::borrow::Cow;
use std::sync::Arc;

/// The region to send requests to.
///
/// The region MUST be specified on a request. It may be configured globally or on a
/// per-client basis unless otherwise noted. A full list of regions is found in the
/// "Regions and Endpoints" document.
///
/// See http://docs.aws.amazon.com/general/latest/gr/rande.html for
/// information on AWS regions.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Region(Arc<String>);
impl AsRef<str> for Region {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

impl Region {
    pub fn new(region: impl Into<String>) -> Self {
        Self(Arc::new(region.into()))
    }
}

/// The region to use when signing requests
///
/// Generally, user code will not need to interact with `SigningRegion`. See `[Region](crate::Region)`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SigningRegion(Arc<String>);
impl AsRef<str> for SigningRegion {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}
pub mod region;

impl From<Region> for SigningRegion {
    fn from(inp: Region) -> Self {
        SigningRegion(inp.0)
    }
}
use std::borrow::Cow;

/// The name of the service used to sign this request
///
+80 −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.
 */

use std::sync::Arc;

/// The region to send requests to.
///
/// The region MUST be specified on a request. It may be configured globally or on a
/// per-client basis unless otherwise noted. A full list of regions is found in the
/// "Regions and Endpoints" document.
///
/// See http://docs.aws.amazon.com/general/latest/gr/rande.html for
/// information on AWS regions.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Region(Arc<String>);

impl AsRef<str> for Region {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

impl Region {
    pub fn new(region: impl Into<String>) -> Self {
        Self(Arc::new(region.into()))
    }
}

/// Provide a [`Region`](Region) to use with AWS requests
///
/// For most cases [`default_provider`](default_provider) will be the best option, implementing
/// a standard provider chain.
pub trait ProvideRegion: Send + Sync {
    fn region(&self) -> Option<Region>;
}

impl ProvideRegion for Region {
    fn region(&self) -> Option<Region> {
        Some(self.clone())
    }
}

pub fn default_provider() -> impl ProvideRegion {
    EnvironmentProvider
}

#[non_exhaustive]
pub struct EnvironmentProvider;

impl EnvironmentProvider {
    pub fn new() -> Self {
        EnvironmentProvider
    }
}

impl ProvideRegion for EnvironmentProvider {
    fn region(&self) -> Option<Region> {
        std::env::var("AWS_DEFAULT_REGION").map(Region::new).ok()
    }
}

/// The region to use when signing requests
///
/// Generally, user code will not need to interact with `SigningRegion`. See `[Region](crate::Region)`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SigningRegion(Arc<String>);

impl AsRef<str> for SigningRegion {
    fn as_ref(&self) -> &str {
        self.0.as_str()
    }
}

impl From<Region> for SigningRegion {
    fn from(inp: Region) -> Self {
        SigningRegion(inp.0)
    }
}
+8 −11
Original line number Diff line number Diff line
@@ -5,17 +5,14 @@

package software.amazon.smithy.rustsdk

import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.smithy.generators.ProtocolConfig
import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator

class AwsCodegenDecorator : RustCodegenDecorator {
    override val name: String = "AwsSdkCodgenDecorator"
val DECORATORS = listOf(
    CredentialsProviderDecorator(),
    RegionDecorator()
)

class AwsCodegenDecorator : CombinedCodegenDecorator(DECORATORS) {
    override val name: String = "AwsSdkCodegenDecorator"
    override val order: Byte = -1
    override fun configCustomizations(
        protocolConfig: ProtocolConfig,
        baseCustomizations: List<ConfigCustomization>
    ): List<ConfigCustomization> {
        return listOf(BaseAwsConfig()) + baseCustomizations
    }
}
+119 −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.
 */

package software.amazon.smithy.rustsdk

import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.Local
import software.amazon.smithy.rust.codegen.rustlang.Writable
import software.amazon.smithy.rust.codegen.rustlang.rust
import software.amazon.smithy.rust.codegen.rustlang.writable
import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.smithy.generators.OperationSection
import software.amazon.smithy.rust.codegen.smithy.generators.ProtocolConfig
import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfig

/* Example Generated Code */
/*
pub struct Config {
    pub region: Option<::aws_types::region::Region>,
}
#[derive(Default)]
pub struct ConfigBuilder {
    region: Option<::aws_types::region::Region>,
}
impl ConfigBuilder {
    pub fn region(mut self, region_provider: impl ::aws_types::region::ProvideRegion) -> Self {
        self.region = region_provider.region();
        self
    }

    pub fn build(self) -> Config {
        Config {
            region: {
                use ::aws_types::region::ProvideRegion;
                self.region
                    .or_else(|| ::aws_types::region::default_provider().region())
            },
    }
}
 */

class RegionDecorator : RustCodegenDecorator {
    override val name: String = "Region"
    override val order: Byte = 0

    override fun configCustomizations(
        protocolConfig: ProtocolConfig,
        baseCustomizations: List<ConfigCustomization>
    ): List<ConfigCustomization> {
        return baseCustomizations + RegionProviderConfig(protocolConfig.runtimeConfig)
    }

    override fun operationCustomizations(
        protocolConfig: ProtocolConfig,
        operation: OperationShape,
        baseCustomizations: List<OperationCustomization>
    ): List<OperationCustomization> {
        return baseCustomizations + RegionConfigPlugin()
    }
}

class RegionProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() {
    private val region = region(runtimeConfig)
    override fun section(section: ServiceConfig) = writable {
        when (section) {
            is ServiceConfig.ConfigStruct -> rust("pub(crate) region: Option<#T::Region>,", region)
            is ServiceConfig.ConfigImpl -> emptySection
            is ServiceConfig.BuilderStruct ->
                rust("region: Option<#T::Region>,", region)
            ServiceConfig.BuilderImpl ->
                rust(
                    """
            pub fn region(mut self, region_provider: impl #1T::ProvideRegion) -> Self {
                self.region = region_provider.region();
                self
            }
            """,
                    region
                )
            ServiceConfig.BuilderBuild -> rust(
                """region: {
                    use #1T::ProvideRegion;
                    self.region.or_else(||#1T::default_provider().region())
                },""",
                region
            )
        }
    }
}

class RegionConfigPlugin() : OperationCustomization() {
    override fun section(section: OperationSection): Writable {
        return when (section) {
            OperationSection.ImplBlock -> emptySection
            is OperationSection.Feature -> writable {
                // Allow the region to be late-inserted via another method
                rust(
                    """
                if let Some(region) = &${section.config}.region {
                    ${section.request}.config_mut().insert(region.clone());
                }
                """
                )
            }
        }
    }
}

fun region(runtimeConfig: RuntimeConfig) =
    RuntimeType("region", awsTypes(runtimeConfig), "aws_types")

fun awsTypes(runtimeConfig: RuntimeConfig) = CargoDependency("aws-types", Local(runtimeConfig.relativePath))
Loading