diff --git a/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClientConfiguration.h b/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClientConfiguration.h index d8586dde26c..49f5a9a449b 100644 --- a/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClientConfiguration.h +++ b/generated/src/aws-cpp-sdk-dynamodb/include/aws/dynamodb/DynamoDBClientConfiguration.h @@ -21,17 +21,13 @@ namespace Aws DynamoDBClientConfiguration(const DynamoDBClientConfiguration& other) : Aws::Client::GenericClientConfiguration(other), - enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery), - accountId{other.accountId}, - accountIdEndpointMode{other.accountIdEndpointMode} + enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery) { } DynamoDBClientConfiguration(DynamoDBClientConfiguration&& other) noexcept : Aws::Client::GenericClientConfiguration(std::move(other)), - enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery), - accountId{std::move(other.accountId)}, - accountIdEndpointMode{std::move(other.accountIdEndpointMode)} + enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery) { } @@ -40,8 +36,6 @@ namespace Aws if (this == &other) return *this; Aws::Client::GenericClientConfiguration::operator =(other); - accountId = other.accountId; - accountIdEndpointMode = other.accountIdEndpointMode; return *this; } @@ -50,8 +44,6 @@ namespace Aws if (this == &other) return *this; Aws::Client::GenericClientConfiguration::operator =(std::move(other)); - accountId = std::move(other.accountId); - accountIdEndpointMode = std::move(other.accountIdEndpointMode); return *this; } @@ -90,14 +82,6 @@ namespace Aws */ Aws::Crt::Optional& enableEndpointDiscovery; - /** - * The AWS AccountId used for the request. - */ - Aws::String accountId; - /** - * The AccountId Endpoint Mode. - */ - Aws::String accountIdEndpointMode = "preferred"; private: void LoadDynamoDBSpecificConfig(const Aws::String& profileName); }; diff --git a/generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClientConfiguration.cpp b/generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClientConfiguration.cpp index 353bfeb192c..709bcc0fe68 100644 --- a/generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClientConfiguration.cpp +++ b/generated/src/aws-cpp-sdk-dynamodb/source/DynamoDBClientConfiguration.cpp @@ -45,15 +45,6 @@ void DynamoDBClientConfiguration::LoadDynamoDBSpecificConfig(const Aws::String& if(!enableEndpointDiscovery) { enableEndpointDiscovery = IsEndpointDiscoveryEnabled(this->endpointOverride, inputProfileName); } - // accountId is intentionally not set here: AWS_ACCOUNT_ID env variable may not match the provided credentials. - // it must be set by an auth provider / identity resolver or by an SDK user. - static const char AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE[] = "AWS_ACCOUNT_ID_ENDPOINT_MODE"; - static const char AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION[] = "account_id_endpoint_mode"; - accountIdEndpointMode = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE, - inputProfileName, - AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION, - {"required", "disabled", "preferred"}, /* allowed values */ - "preferred" /* default value */); } DynamoDBClientConfiguration::DynamoDBClientConfiguration(const Client::ClientConfigurationInitValues &configuration) diff --git a/generated/src/aws-cpp-sdk-s3control/include/aws/s3control/S3ControlClientConfiguration.h b/generated/src/aws-cpp-sdk-s3control/include/aws/s3control/S3ControlClientConfiguration.h index 73bb1e63557..fcb0c09b58f 100644 --- a/generated/src/aws-cpp-sdk-s3control/include/aws/s3control/S3ControlClientConfiguration.h +++ b/generated/src/aws-cpp-sdk-s3control/include/aws/s3control/S3ControlClientConfiguration.h @@ -18,36 +18,6 @@ namespace Aws { using BaseClientConfigClass = Aws::Client::GenericClientConfiguration; - S3ControlClientConfiguration(const S3ControlClientConfiguration& other) - : Aws::Client::GenericClientConfiguration(other), - accountId{other.accountId} - { - } - - S3ControlClientConfiguration(S3ControlClientConfiguration&& other) noexcept - : Aws::Client::GenericClientConfiguration(std::move(other)), - accountId{std::move(other.accountId)} - { - } - - S3ControlClientConfiguration& operator=(const S3ControlClientConfiguration& other) - { - if (this == &other) - return *this; - Aws::Client::GenericClientConfiguration::operator =(other); - accountId = other.accountId; - return *this; - } - - S3ControlClientConfiguration& operator=(S3ControlClientConfiguration&& other) noexcept - { - if (this == &other) - return *this; - Aws::Client::GenericClientConfiguration::operator =(std::move(other)); - accountId = std::move(other.accountId); - return *this; - } - S3ControlClientConfiguration(const Client::ClientConfigurationInitValues &configuration = {}); /** @@ -72,10 +42,6 @@ namespace Aws S3ControlClientConfiguration(const Client::ClientConfiguration& config); bool useArnRegion = false; Client::AWSAuthV4Signer::PayloadSigningPolicy payloadSigningPolicy = Client::AWSAuthV4Signer::PayloadSigningPolicy::RequestDependent; - /** - * The Account ID used to send the request. This is an optional parameter that will be set automatically for operations that require it. - */ - Aws::String accountId; private: void LoadS3ControlSpecificConfig(const Aws::String& profileName); }; diff --git a/generated/src/aws-cpp-sdk-s3control/source/S3ControlClientConfiguration.cpp b/generated/src/aws-cpp-sdk-s3control/source/S3ControlClientConfiguration.cpp index 9289b07fa28..d34c1a61354 100644 --- a/generated/src/aws-cpp-sdk-s3control/source/S3ControlClientConfiguration.cpp +++ b/generated/src/aws-cpp-sdk-s3control/source/S3ControlClientConfiguration.cpp @@ -24,8 +24,6 @@ void S3ControlClientConfiguration::LoadS3ControlSpecificConfig(const Aws::String { useArnRegion = true; } - // accountId is intentionally not set here: AWS_ACCOUNT_ID env variable may not match the provided credentials. - // it must be set by an auth provider / identity resolver or by an SDK user. } S3ControlClientConfiguration::S3ControlClientConfiguration(const Client::ClientConfigurationInitValues &configuration) diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h index 13e5458da7b..9b0fd8de6a2 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h @@ -465,6 +465,16 @@ namespace Aws */ bool useAnonymousAuth = false; } winHTTPOptions; + + /** + * The AWS AccountId used for the request. + */ + Aws::String accountId; + + /** + * The AccountId Endpoint Mode. + */ + Aws::String accountIdEndpointMode = "preferred"; }; /** diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h b/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h index 70e83c178cb..81f630b7dfb 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/UserAgent.h @@ -26,6 +26,10 @@ enum class UserAgentFeature { FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED, FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED, FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED, + ACCOUNT_ID_MODE_PREFERRED, + ACCOUNT_ID_MODE_DISABLED, + ACCOUNT_ID_MODE_REQUIRED, + RESOLVED_ACCOUNT_ID, }; class AWS_CORE_API UserAgent { diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h index 5be45b494fc..ced88664d14 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h @@ -346,32 +346,20 @@ namespace client ResponseT, ErrorMarshallerT>>; - /** - * SFINAE implementation for refreshing client context params enabled if the client configuration - * type has a AccountId member. If there is a corresponding member we want to use that in the endpoint - * calculation and set it as a client context parameter. - */ - template - struct HasAccountId : std::false_type {}; - - template - struct HasAccountId().accountId))> : std::true_type {}; - - template::value, int>::type = 0> GetContextEndpointParametersOutcome GetContextEndpointParametersImpl(const AwsSmithyClientAsyncRequestContext& ctx) const { Aws::Vector endpointParameters; const auto resolvedAccountId = ctx.m_awsIdentity->accountId(); - if (resolvedAccountId.has_value() && !resolvedAccountId.value().empty() && m_clientConfiguration.accountId.empty()) { + const auto resolvedNonEmptyAccountId = resolvedAccountId.has_value() && !resolvedAccountId.value().empty(); + // Set user agent if account ID was resolved in identity provider + if (resolvedNonEmptyAccountId) { + ctx.m_pRequest->AddUserAgentFeature(Aws::Client::UserAgentFeature::RESOLVED_ACCOUNT_ID); + } + // Only set EP param if client configuration does not have a configured account ID and we resolved a account id + if (resolvedNonEmptyAccountId && m_clientConfiguration.accountId.empty()) { endpointParameters.emplace_back("AccountId", resolvedAccountId.value(), Aws::Endpoint::EndpointParameter::ParameterOrigin::OPERATION_CONTEXT); } return endpointParameters; } - - template::value, int>::type = 0> - GetContextEndpointParametersOutcome GetContextEndpointParametersImpl(const AwsSmithyClientAsyncRequestContext& ctx) const { - AWS_UNREFERENCED_PARAM(ctx); - return Aws::Vector{}; - } }; } // namespace client diff --git a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp index 784c4c8b70a..4e114b1bd21 100644 --- a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp +++ b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp @@ -39,6 +39,8 @@ static const char* REQUEST_MIN_COMPRESSION_SIZE_BYTES_CONFIG_VAR = "request_min_ static const char* AWS_EXECUTION_ENV = "AWS_EXECUTION_ENV"; static const char* DISABLE_IMDSV1_CONFIG_VAR = "AWS_EC2_METADATA_V1_DISABLED"; static const char* DISABLE_IMDSV1_ENV_VAR = "ec2_metadata_v1_disabled"; +static const char* AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE = "AWS_ACCOUNT_ID_ENDPOINT_MODE"; +static const char* AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION = "account_id_endpoint_mode"; using RequestChecksumConfigurationEnumMapping = std::pair; static const std::array REQUEST_CHECKSUM_CONFIG_MAPPING = {{ @@ -278,12 +280,21 @@ void setConfigFromEnvOrProfile(ClientConfiguration &config) if (disableIMDSv1 == "true") { config.disableImdsV1 = true; } + + // accountId is intentionally not set here: AWS_ACCOUNT_ID env variable may not match the provided credentials. + // it must be set by an auth provider / identity resolver or by an SDK user. + config.accountIdEndpointMode = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE, + config.profileName, + AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION, + {"required", "disabled", "preferred"}, /* allowed values */ + "preferred" /* default value */); } ClientConfiguration::ClientConfiguration() { this->disableIMDS = false; setLegacyClientConfigurationParameters(*this); + setConfigFromEnvOrProfile(*this);; if (!this->disableIMDS && region.empty() && @@ -300,13 +311,13 @@ ClientConfiguration::ClientConfiguration() return; } region = Aws::String(Aws::Region::US_EAST_1); - setConfigFromEnvOrProfile(*this); } ClientConfiguration::ClientConfiguration(const ClientConfigurationInitValues &configuration) { this->disableIMDS = configuration.shouldDisableIMDS; setLegacyClientConfigurationParameters(*this); + setConfigFromEnvOrProfile(*this); if (!this->disableIMDS && region.empty() && @@ -323,7 +334,6 @@ ClientConfiguration::ClientConfiguration(const ClientConfigurationInitValues &co return; } region = Aws::String(Aws::Region::US_EAST_1); - setConfigFromEnvOrProfile(*this); } ClientConfiguration::ClientConfiguration(const char* profile, bool shouldDisableIMDS) @@ -333,6 +343,7 @@ ClientConfiguration::ClientConfiguration(const char* profile, bool shouldDisable this->profileName = Aws::String(profile); } setLegacyClientConfigurationParameters(*this); + setConfigFromEnvOrProfile(*this); // Call EC2 Instance Metadata service only once Aws::String ec2MetadataRegion; bool hasEc2MetadataRegion = false; @@ -368,13 +379,13 @@ ClientConfiguration::ClientConfiguration(const char* profile, bool shouldDisable } AWS_LOGSTREAM_WARN(CLIENT_CONFIG_TAG, "User specified profile: [" << profile << "] is not found, will use the SDK resolved one."); - setConfigFromEnvOrProfile(*this); } ClientConfiguration::ClientConfiguration(bool /*useSmartDefaults*/, const char* defaultMode, bool shouldDisableIMDS) { this->disableIMDS = shouldDisableIMDS; setLegacyClientConfigurationParameters(*this); + setConfigFromEnvOrProfile(*this); // Call EC2 Instance Metadata service only once Aws::String ec2MetadataRegion; @@ -397,7 +408,6 @@ ClientConfiguration::ClientConfiguration(bool /*useSmartDefaults*/, const char* } Aws::Config::Defaults::SetSmartDefaultsConfigurationParameters(*this, defaultMode, hasEc2MetadataRegion, ec2MetadataRegion); - setConfigFromEnvOrProfile(*this); } std::shared_ptr InitRetryStrategy(Aws::String retryMode) diff --git a/src/aws-cpp-sdk-core/source/client/UserAgent.cpp b/src/aws-cpp-sdk-core/source/client/UserAgent.cpp index f0cc5adc08c..694eab676c6 100644 --- a/src/aws-cpp-sdk-core/source/client/UserAgent.cpp +++ b/src/aws-cpp-sdk-core/source/client/UserAgent.cpp @@ -9,7 +9,9 @@ #include #include #include +#include +#include #include #include @@ -33,7 +35,11 @@ const std::pair BUSINESS_METRIC_MAPPING[] = { {UserAgentFeature::FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED, "Z"}, {UserAgentFeature::FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED, "a"}, {UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED, "b"}, - {UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED, "c"} + {UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED, "c"}, + {UserAgentFeature::ACCOUNT_ID_MODE_PREFERRED, "P"}, + {UserAgentFeature::ACCOUNT_ID_MODE_DISABLED , "Q"}, + {UserAgentFeature::ACCOUNT_ID_MODE_REQUIRED, "R"}, + {UserAgentFeature::RESOLVED_ACCOUNT_ID, "T"}, }; Aws::String BusinessMetricForFeature(UserAgentFeature feature) { @@ -53,6 +59,22 @@ const std::pair RETRY_FEATURE_MAPPING[] = { {"adaptive", UserAgentFeature::RETRY_MODE_ADAPTIVE}, }; +const std::pair ACCOUNT_ID_MODE_MAPPING[] = { + {"preferred", UserAgentFeature::ACCOUNT_ID_MODE_PREFERRED}, + {"disabled", UserAgentFeature::ACCOUNT_ID_MODE_DISABLED}, + {"required", UserAgentFeature::ACCOUNT_ID_MODE_REQUIRED}, +}; + +Aws::Crt::Optional BusinessMetricForAccountIdMode(const Aws::String& accountIdMode) { + const auto *const accountIdFeature = std::find_if(std::begin(ACCOUNT_ID_MODE_MAPPING), + std::end(ACCOUNT_ID_MODE_MAPPING), + [&accountIdMode](const std::pair& mapping) -> bool { return mapping.first == accountIdMode; }); + if (accountIdFeature == std::end(ACCOUNT_ID_MODE_MAPPING)) { + return {}; + } + return accountIdFeature->second; +} + const char* EXEC_ENV_VAR = "AWS_EXECUTION_ENV"; const char* METADATA = "md"; const char* METADATA_DELIMINATOR = "/"; @@ -94,6 +116,10 @@ UserAgent::UserAgent(const ClientConfiguration& clientConfiguration, #undef XSTR #endif { + const auto accountIdMode = BusinessMetricForAccountIdMode(clientConfiguration.accountIdEndpointMode); + if (accountIdMode.has_value()) { + m_features.emplace(accountIdMode.value()); + } } Aws::String UserAgent::SerializeWithFeatures(const Aws::Set& features) const { diff --git a/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp b/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp index 705fd85148b..4419ed1c0ce 100644 --- a/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp +++ b/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -449,6 +450,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseStandardEndpointIfAccountIdMissingFromCredenti EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://dynamodb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") == features.end()); } @@ -487,6 +494,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromCredentialsFile) EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://spike-spiegel.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromEnviornmentCredentialsProvider) @@ -521,6 +534,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromEnviornmentCredentialsPro EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://ein.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromProcessCredentialsProvider) @@ -555,6 +574,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromProcessCredentialsProvide EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://faye.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromSTSCredentialsProvider) @@ -606,6 +631,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromSTSCredentialsProvider) EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://rocco.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromSSOCredentialsProvider) @@ -672,6 +703,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromSSOCredentialsProvider) EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://jet.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromContainerCredentialsProvider) @@ -704,6 +741,12 @@ TEST_F(DynamoDBUnitTest, ShouldUseAccountIDEndpointFromContainerCredentialsProvi EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://Vicious.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "P") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } TEST_F(DynamoDBUnitTest, ShouldNotUseAccountIDEndpointWhenDisabled) @@ -739,4 +782,84 @@ TEST_F(DynamoDBUnitTest, ShouldNotUseAccountIDEndpointWhenDisabled) EXPECT_TRUE(listTablesOutcome.IsSuccess()); const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); EXPECT_EQ("https://dynamodb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "Q") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); +} + +TEST_F(DynamoDBUnitTest, ShouldWorkWhenNoAccountIDAndRequired) +{ + // create enviornment variables with account id + const Environment::EnvironmentRAII environmentVariables{ + { + {"AWS_ACCESS_KEY_ID", "the"}, + {"AWS_SECRET_ACCESS_KEY", "real"}, + {"AWS_SESSION_TOKEN", "folk"}, + }}; + + DynamoDBClientConfiguration configuration; + configuration.region = "us-east-1"; + configuration.accountIdEndpointMode = "required"; + + auto credsProvider = Aws::MakeShared(LOG_TAG); + + const auto accountIdClient = Aws::MakeShared(LOG_TAG, std::move(credsProvider), nullptr, configuration); + + // mock response + auto successStream = Aws::MakeShared(LOG_TAG, "cowboy.bebop/planets", HttpMethod::HTTP_GET); + successStream->SetResponseStreamFactory([]() -> IOStream* { + auto listTablesString = R"({"LastEvaluatedTableName": "Planets","TableNames": ["Planets"]}))"; + return Aws::New(LOG_TAG, listTablesString, std::ios_base::in | std::ios_base::binary); + }); + auto successResponse = Aws::MakeShared(LOG_TAG, successStream); + successResponse->SetResponseCode(HttpResponseCode::OK); + + mock_http_client_->AddResponseToReturn(successResponse); + const auto listTablesOutcome = accountIdClient->ListTables(); + EXPECT_TRUE(!listTablesOutcome.IsSuccess()); + EXPECT_EQ(listTablesOutcome.GetError().GetMessage(), "AccountIdEndpointMode is required but no AccountID was provided or able to be loaded"); +} + +TEST_F(DynamoDBUnitTest, ShouldFailWhenNoAccountIDAndRequired) +{ + // create enviornment variables with account id + const Environment::EnvironmentRAII environmentVariables{ + { + {"AWS_ACCESS_KEY_ID", "the"}, + {"AWS_SECRET_ACCESS_KEY", "real"}, + {"AWS_SESSION_TOKEN", "folk"}, + {"AWS_ACCOUNT_ID", "blues"}, + }}; + + DynamoDBClientConfiguration configuration; + configuration.region = "us-east-1"; + configuration.accountIdEndpointMode = "required"; + + auto credsProvider = Aws::MakeShared(LOG_TAG); + + const auto accountIdClient = Aws::MakeShared(LOG_TAG, std::move(credsProvider), nullptr, configuration); + + // mock response + auto successStream = Aws::MakeShared(LOG_TAG, "cowboy.bebop/planets", HttpMethod::HTTP_GET); + successStream->SetResponseStreamFactory([]() -> IOStream* { + auto listTablesString = R"({"LastEvaluatedTableName": "Planets","TableNames": ["Planets"]}))"; + return Aws::New(LOG_TAG, listTablesString, std::ios_base::in | std::ios_base::binary); + }); + auto successResponse = Aws::MakeShared(LOG_TAG, successStream); + successResponse->SetResponseCode(HttpResponseCode::OK); + + mock_http_client_->AddResponseToReturn(successResponse); + const auto listTablesOutcome = accountIdClient->ListTables(); + EXPECT_TRUE(listTablesOutcome.IsSuccess()); + const auto requestSeen = mock_http_client_->GetMostRecentHttpRequest(); + EXPECT_EQ("https://blues.ddb.us-east-1.amazonaws.com", requestSeen.GetUri().GetURIString()); + const auto features = GetFeaturesForRequest(requestSeen); + EXPECT_TRUE(!features.empty()); + // AccountId is disabled + EXPECT_TRUE(std::find(features.begin(), features.end(), "R") != features.end()); + // Identity resolved a accountId + EXPECT_TRUE(std::find(features.begin(), features.end(), "T") != features.end()); } \ No newline at end of file diff --git a/tests/testing-resources/CMakeLists.txt b/tests/testing-resources/CMakeLists.txt index 0711dd14a8d..59bd8345072 100644 --- a/tests/testing-resources/CMakeLists.txt +++ b/tests/testing-resources/CMakeLists.txt @@ -19,6 +19,7 @@ file(GLOB AWS_TESTING_HTTP_MOCKS_HEADERS "include/aws/testing/mocks/http/*.h") file(GLOB AWS_TESTING_CLIENT_MOCKS_HEADERS "include/aws/testing/mocks/aws/client/*.h") file(GLOB AWS_TESTING_MONITORING_MOCKS_HEADERS "include/aws/testing/mocks/monitoring/*.h") file(GLOB AWS_TESTING_PLATFORM_HEADERS "include/aws/testing/platform/*.h") +file(GLOB AWS_TESTING_UTILS_HEADERS "include/aws/testing/utils/*.h") file(GLOB AWS_TESTING_SOURCE "source/*.cpp") file(GLOB AWS_TESTING_EXTERNAL_SOURCE "source/external/*.cc") @@ -36,6 +37,7 @@ file(GLOB TestingResources_SHARED_SRC ${AWS_TESTING_CLIENT_MOCKS_HEADERS} ${AWS_TESTING_MONITORING_MOCKS_HEADERS} ${AWS_TESTING_PLATFORM_HEADERS} + ${AWS_TESTING_UTILS_HEADERS} ${AWS_TESTING_SOURCE} ${AWS_TESTING_EXTERNAL_SOURCE} ${AWS_TESTING_GTEST_SOURCE} @@ -59,6 +61,7 @@ if(PLATFORM_WINDOWS) if(MSVC) source_group("Header Files\\aws\\testing" FILES ${AWS_TESTING_HEADERS}) source_group("Header Files\\aws\\testing\\platform" FILES ${AWS_TESTING_PLATFORM_HEADERS}) + source_group("Header Files\\aws\\testing\\utils" FILES ${AWS_TESTING_UTILS_HEADERS}) source_group("Header Files\\aws\\external" FILES ${AWS_TESTING_EXTERNAL_HEADERS}) source_group("Header Files\\aws\\external\\gtest" FILES ${AWS_TESTING_GTEST_HEADERS}) source_group("Header Files\\aws\\external\\gtest\\internal" FILES ${AWS_TESTING_GTEST_HEADERS_INTERNAL}) @@ -148,6 +151,7 @@ install (FILES ${AWS_TESTING_GTEST_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws install (FILES ${AWS_TESTING_GTEST_HEADERS_INTERNAL} DESTINATION ${INCLUDE_DIRECTORY}/aws/external/gtest/internal) install (FILES ${AWS_TESTING_GTEST_HEADERS_INTERNAL_CUSTOM} DESTINATION ${INCLUDE_DIRECTORY}/aws/external/gtest/internal/custom) install (FILES ${AWS_TESTING_PLATFORM_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/testing/platform) +install (FILES ${AWS_TESTING_UTILS_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/testing/utils) install (FILES ${AWS_TESTING_AUTH_MOCKS_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/testing/mocks/aws/auth) install (FILES ${AWS_TESTING_CLIENT_MOCKS_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/testing/mocks/aws/client) install (FILES ${AWS_TESTING_EVENT_MOCKS_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/aws/testing/mocks/event) diff --git a/tests/testing-resources/include/aws/testing/utils/UserAgentUtils.h b/tests/testing-resources/include/aws/testing/utils/UserAgentUtils.h new file mode 100644 index 00000000000..4bcdb2b617e --- /dev/null +++ b/tests/testing-resources/include/aws/testing/utils/UserAgentUtils.h @@ -0,0 +1,26 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once +#include +#include + +#include + +inline Aws::Vector GetFeaturesForRequest(const Aws::Http::HttpRequest &request) { + const auto& userAgentString = request.GetHeaderValue(Aws::Http::USER_AGENT_HEADER); + EXPECT_TRUE(!userAgentString.empty()); + const auto userAgentValues = Aws::Utils::StringUtils::Split(userAgentString, ' '); + const Aws::String businessValuePrefix{"m/"}; + auto businessMetrics = std::find_if( + userAgentValues.begin(), userAgentValues.end(), + [&businessValuePrefix](const Aws::String& value) -> bool { return value.find(businessValuePrefix) != Aws::String::npos; }); + EXPECT_TRUE(businessMetrics != userAgentValues.end()); + const auto metrics = [](Aws::String str, const Aws::String& sub) -> Aws::String { + str.replace(str.find(sub), sub.length(), ""); + return str; + }(*businessMetrics, businessValuePrefix); + return Aws::Utils::StringUtils::Split(metrics, ','); +} \ No newline at end of file diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationHeader.vm index 2ef845cb29e..16d0f153ee6 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationHeader.vm @@ -58,18 +58,6 @@ namespace ${rootNamespace} #set($addArgDummy = $copyCtorStatements.add("enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery)")) #set($addArgDummy = $moveCtorStatements.add("enableEndpointDiscovery(BaseClientConfigClass::enableEndpointDiscovery)")) #end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountId")) -#set($addArgDummy = $copyCtorStatements.add("accountId{other.accountId}")) -#set($addArgDummy = $moveCtorStatements.add("accountId{std::move(other.accountId)}")) -#set($addArgDummy = $copyAssignmentStatements.add("accountId = other.accountId;")) -#set($addArgDummy = $moveAssignemntStatements.add("accountId = std::move(other.accountId);")) -#end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountIdEndpointMode")) -#set($addArgDummy = $copyCtorStatements.add("accountIdEndpointMode{other.accountIdEndpointMode}")) -#set($addArgDummy = $moveCtorStatements.add("accountIdEndpointMode{std::move(other.accountIdEndpointMode)}")) -#set($addArgDummy = $copyAssignmentStatements.add("accountIdEndpointMode = other.accountIdEndpointMode;")) -#set($addArgDummy = $moveAssignemntStatements.add("accountIdEndpointMode = std::move(other.accountIdEndpointMode);")) -#end #if(!$copyCtorStatements.isEmpty()) ${metadata.classNamePrefix}ClientConfiguration(const ${metadata.classNamePrefix}ClientConfiguration& other) : Aws::Client::GenericClientConfiguration(other), @@ -187,18 +175,6 @@ namespace ${rootNamespace} Aws::Crt::Optional& enableEndpointDiscovery; #end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountId")) - /** - * $serviceModel.endpointRuleSetModel.parameters["AccountId"].documentation - */ - Aws::String accountId; -#end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountIdEndpointMode")) - /** - * $serviceModel.endpointRuleSetModel.parameters["AccountIdEndpointMode"].documentation - */ - Aws::String accountIdEndpointMode = "preferred"; -#end #if($serviceNamespace == "S3Crt") #parse("com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtClientConfigHeader.vm") #end diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationSource.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationSource.vm index 123190b86e6..6f94e2e2202 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationSource.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/common/ServiceClientConfigurationSource.vm @@ -123,19 +123,6 @@ void ${metadata.classNamePrefix}ClientConfiguration::Load${serviceNamespace}Spec enableEndpointDiscovery = IsEndpointDiscoveryEnabled(this->endpointOverride, inputProfileName); } #end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountId")) - // accountId is intentionally not set here: AWS_ACCOUNT_ID env variable may not match the provided credentials. - // it must be set by an auth provider / identity resolver or by an SDK user. -#end -#if($serviceModel.endpointRuleSetModel.parameters.containsKey("AccountIdEndpointMode")) - static const char AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE[] = "AWS_ACCOUNT_ID_ENDPOINT_MODE"; - static const char AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION[] = "account_id_endpoint_mode"; - accountIdEndpointMode = ClientConfiguration::LoadConfigFromEnvOrProfile(AWS_ACCOUNT_ID_ENDPOINT_MODE_ENVIRONMENT_VARIABLE, - inputProfileName, - AWS_ACCOUNT_ID_ENDPOINT_MODE_CONFIG_FILE_OPTION, - {"required", "disabled", "preferred"}, /* allowed values */ - "preferred" /* default value */); -#end } ${metadata.classNamePrefix}ClientConfiguration::${metadata.classNamePrefix}ClientConfiguration(const Client::ClientConfigurationInitValues &configuration)