diff --git a/cmake/sdksCommon.cmake b/cmake/sdksCommon.cmake index 3173a54a838..7eb26f8e40b 100644 --- a/cmake/sdksCommon.cmake +++ b/cmake/sdksCommon.cmake @@ -95,7 +95,7 @@ list(APPEND SDK_TEST_PROJECT_LIST "elasticfilesystem:tests/aws-cpp-sdk-elasticfi list(APPEND SDK_TEST_PROJECT_LIST "identity-management:tests/aws-cpp-sdk-identity-management-tests") list(APPEND SDK_TEST_PROJECT_LIST "kinesis:tests/aws-cpp-sdk-kinesis-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "lambda:tests/aws-cpp-sdk-lambda-integration-tests") -list(APPEND SDK_TEST_PROJECT_LIST "logs:tests/aws-cpp-sdk-logs-integration-tests") +list(APPEND SDK_TEST_PROJECT_LIST "logs:tests/aws-cpp-sdk-logs-integration-tests,tests/aws-cpp-sdk-logs-unit-tests") list(APPEND SDK_TEST_PROJECT_LIST "mediastore-data:tests/aws-cpp-sdk-mediastore-data-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "monitoring:tests/aws-cpp-sdk-monitoring-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "rds:tests/aws-cpp-sdk-rds-integration-tests") 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 0b79aceda1a..fd5ac8ee8ee 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h @@ -60,55 +60,78 @@ namespace client m_authSchemes(authSchemes), m_serializer(Aws::MakeShared(ServiceNameT, m_clientConfiguration.telemetryProvider)) { - m_serviceName = ServiceNameT; initClient(); } - AwsSmithyClientT(const AwsSmithyClientT& other): - AwsSmithyClientBase(other, - Aws::MakeUnique(ServiceNameT, other.m_clientConfiguration), - ServiceNameT, - other.m_serviceUserAgentName, - Aws::Http::CreateHttpClient(other.m_clientConfiguration), - Aws::MakeShared(ServiceNameT)), - m_clientConfiguration{*static_cast(m_clientConfig.get())}, - m_endpointProvider{other.m_endpointProvider}, - m_authSchemeResolver{Aws::MakeShared(ServiceNameT)}, - m_authSchemes{other.m_authSchemes}, - m_serializer{Aws::MakeShared(ServiceNameT, m_clientConfiguration.telemetryProvider)} + AwsSmithyClientT(const AwsSmithyClientT& other) + : AwsSmithyClientBase(other, + Aws::MakeUnique(ServiceNameT, other.m_clientConfiguration), + Aws::Http::CreateHttpClient(other.m_clientConfiguration), + Aws::MakeShared(ServiceNameT)), + m_clientConfiguration(*static_cast(m_clientConfig.get())) { - initClient(); + m_endpointProvider = other.m_endpointProvider; /* shallow copy */ + m_authSchemeResolver = other.m_authSchemeResolver; /* shallow copy */ + m_authSchemes = other.m_authSchemes; + m_serializer = Aws::MakeShared(ServiceNameT, m_clientConfiguration.telemetryProvider); + initClient(); } AwsSmithyClientT& operator=(const AwsSmithyClientT& other) { if(this != &other) { - AwsSmithyClientBase::deepCopy(Aws::MakeUnique(ServiceNameT, other.m_clientConfiguration), - ServiceNameT, - Aws::Http::CreateHttpClient(other.m_clientConfiguration), + m_clientConfiguration = other.m_clientConfiguration; + AwsSmithyClientBase::baseCopyAssign(other, + Aws::Http::CreateHttpClient(m_clientConfiguration), Aws::MakeShared(ServiceNameT)); - m_clientConfiguration = *static_cast(m_clientConfig.get()); - m_endpointProvider = other.m_endpointProvider; - m_authSchemeResolver = Aws::MakeShared(ServiceNameT); + + m_endpointProvider = other.m_endpointProvider; /* shallow copy */ + m_authSchemeResolver = other.m_authSchemeResolver; /* shallow copy */ m_authSchemes = other.m_authSchemes; m_serializer = Aws::MakeShared(ServiceNameT, m_clientConfiguration.telemetryProvider); - m_errorMarshaller = Aws::MakeShared(ServiceNameT); initClient(); } return *this; } - AwsSmithyClientT (AwsSmithyClientT&&) = default; + AwsSmithyClientT(AwsSmithyClientT&& other) : + AwsSmithyClientBase(std::move(static_cast(other)), + Aws::MakeUnique(ServiceNameT, std::move(other.m_clientConfiguration))), + m_clientConfiguration{*static_cast(m_clientConfig.get())}, + m_endpointProvider(std::move(other.m_endpointProvider)), + m_authSchemeResolver(std::move(other.m_authSchemeResolver)), + m_authSchemes(std::move(other.m_authSchemes)), + m_serializer(std::move(other.m_serializer)) + { + } - AwsSmithyClientT& operator=(AwsSmithyClientT&&) = default; + AwsSmithyClientT& operator=(AwsSmithyClientT&& other) + { + if(this != &other) + { + m_clientConfiguration = std::move(other.m_clientConfiguration); + AwsSmithyClientBase::baseMoveAssign(std::move(static_cast(other))); + + m_endpointProvider = std::move(other.m_endpointProvider); + m_authSchemeResolver = std::move(other.m_authSchemeResolver); + m_authSchemes = std::move(other.m_authSchemes); + m_serializer = std::move(other.m_serializer); + } + return *this; + } virtual ~AwsSmithyClientT() = default; protected: void initClient() { - m_endpointProvider->InitBuiltInParameters(m_clientConfiguration); - m_authSchemeResolver->Init(m_clientConfiguration); + if (m_endpointProvider && m_authSchemeResolver) { + m_endpointProvider->InitBuiltInParameters(m_clientConfiguration); + m_authSchemeResolver->Init(m_clientConfiguration); + } else { + AWS_LOGSTREAM_FATAL(ServiceNameT, "Unable to init client: endpoint provider=" << m_endpointProvider + << " or " << "authSchemeResolver=" << m_authSchemeResolver << " are null!"); + } } inline const char* GetServiceClientName() const override { return m_serviceName.c_str(); } @@ -226,7 +249,7 @@ namespace client const Aws::Http::URI& uri = endpoint.GetURI(); auto signerRegionOverride = region; auto signerServiceNameOverride = serviceName; - //signer name is needed for some identity resolvers + // signer name is needed for some identity resolvers if (endpoint.GetAttributes()) { if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) { signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str(); @@ -242,6 +265,10 @@ namespace client } protected: + /* Service client specific config, the actual object is stored in AwsSmithyClientBase by pointer + * In order to avoid config object duplication, smithy template client access it by a reference. + * So that base client has it by base config pointer, child smithy client has it by child config reference. + */ ServiceClientConfigurationT& m_clientConfiguration; std::shared_ptr m_endpointProvider{}; std::shared_ptr m_authSchemeResolver{}; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h index f6e6e436e70..eee6f68139f 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h @@ -85,6 +85,7 @@ namespace client using ResolveEndpointOutcome = Aws::Utils::Outcome; using StreamOutcome = Aws::Utils::Outcome, AWSError >; + /* primary constructor */ AwsSmithyClientBase(Aws::UniquePtr&& clientConfig, Aws::String serviceName, Aws::String serviceUserAgentName, @@ -100,27 +101,31 @@ namespace client baseInit(); } + /* copy constructor substitute */ AwsSmithyClientBase(const AwsSmithyClientBase& other, Aws::UniquePtr&& clientConfig, - Aws::String serviceName, - Aws::String serviceUserAgentName, std::shared_ptr httpClient, std::shared_ptr errorMarshaller) : - m_clientConfig(std::move(clientConfig)), - m_serviceName(std::move(serviceName)), - m_serviceUserAgentName(std::move(serviceUserAgentName)), - m_httpClient(std::move(httpClient)), - m_errorMarshaller(std::move(errorMarshaller)), - m_interceptors{Aws::MakeShared("AwsSmithyClientBase", *m_clientConfig)} + m_clientConfig(std::move(clientConfig)) + { + // this c-tor needs httpClient and errorMarshaller passed explicitly because this base class stores them + // by their parent pointer classes + // and base client class has no idea how to re-create or copy them, and "lombok toBuilder" is not a thing in cpp. + baseCopyAssign(other, std::move(httpClient), std::move(errorMarshaller)); + } + + /* move constructor substitute */ + AwsSmithyClientBase(AwsSmithyClientBase&& other, + Aws::UniquePtr&& clientConfig) : + m_clientConfig(std::move(clientConfig)) { - AWS_UNREFERENCED_PARAM(other); - baseCopyInit(); + baseMoveAssign(std::move(other)); } AwsSmithyClientBase(AwsSmithyClientBase& target) = delete; AwsSmithyClientBase& operator=(AwsSmithyClientBase& target) = delete; - AwsSmithyClientBase(AwsSmithyClientBase&& target) = default; - AwsSmithyClientBase& operator=(AwsSmithyClientBase&& target) = default; + AwsSmithyClientBase(AwsSmithyClientBase&& target) = delete; + AwsSmithyClientBase& operator=(AwsSmithyClientBase&& target) = delete; virtual ~AwsSmithyClientBase() = default; @@ -144,19 +149,6 @@ namespace client void AppendToUserAgent(const Aws::String& valueToAppend); protected: - void deepCopy(Aws::UniquePtr&& clientConfig, - const Aws::String& serviceName, - std::shared_ptr httpClient, - std::shared_ptr errorMarshaller) - { - m_clientConfig = std::move(clientConfig); - m_serviceName = serviceName; - m_httpClient = std::move(httpClient); - m_errorMarshaller = std::move(errorMarshaller); - m_interceptors = Aws::Vector>{Aws::MakeShared("AwsSmithyClientBase")}; - baseCopyInit(); - } - /** * Initialize client configuration with their factory method, unless the user has explicitly set the * configuration, and it is to be shallow copied between different clients, in which case, delete the @@ -170,6 +162,18 @@ namespace client */ void baseCopyInit(); + /** + * A helper utility to re-initialize on copy assignment + */ + void baseCopyAssign(const AwsSmithyClientBase& other, + std::shared_ptr httpClient, + std::shared_ptr errorMarshaller); + + /** + * A helper utility to move assign base client + */ + void baseMoveAssign(AwsSmithyClientBase&& other); + /** * Transforms the AmazonWebServicesResult object into an HttpRequest. */ @@ -191,7 +195,10 @@ namespace client virtual SigningOutcome SignHttpRequest(std::shared_ptr httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const = 0; virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0; - std::shared_ptr m_clientConfig; + /* AwsSmithyClientT class binds its config reference to this pointer, so don't remove const and don't re-allocate it. + * This is done to avoid duplication of config object between this base and actual service template classes. + */ + const Aws::UniquePtr m_clientConfig; Aws::String m_serviceName; Aws::String m_serviceUserAgentName; diff --git a/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp b/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp index 4bdee754245..f6b03491d7f 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp @@ -7,20 +7,22 @@ #include #include #include +#include +#include -#include "aws/core/client/AWSErrorMarshaller.h" -#include "aws/core/client/RetryStrategy.h" -#include "aws/core/http/HttpClientFactory.h" -#include "aws/core/monitoring/CoreMetrics.h" -#include "aws/core/monitoring/MonitoringManager.h" -#include "aws/core/utils/DNS.h" -#include "aws/core/utils/threading/Executor.h" -#include "aws/core/utils/threading/SameThreadExecutor.h" -#include "smithy/tracing/TracingUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include -#include -#include + using namespace smithy::client; using namespace smithy::interceptor; @@ -62,6 +64,7 @@ void createFromFactoriesIfPresent(T& entity, std::function& factory) { } // namespace void AwsSmithyClientBase::baseInit() { + AWS_CHECK_PTR(AWS_SMITHY_CLIENT_LOG, m_clientConfig); createFromFactories(m_clientConfig->retryStrategy, m_clientConfig->configFactories.retryStrategyCreateFn); createFromFactories(m_clientConfig->executor, m_clientConfig->configFactories.executorCreateFn); createFromFactories(m_clientConfig->writeRateLimiter, m_clientConfig->configFactories.writeRateLimiterCreateFn); @@ -77,6 +80,7 @@ void AwsSmithyClientBase::baseInit() { } void AwsSmithyClientBase::baseCopyInit() { + AWS_CHECK_PTR(AWS_SMITHY_CLIENT_LOG, m_clientConfig); createFromFactoriesIfPresent(m_clientConfig->retryStrategy, m_clientConfig->configFactories.retryStrategyCreateFn); createFromFactoriesIfPresent(m_clientConfig->executor, m_clientConfig->configFactories.executorCreateFn); createFromFactoriesIfPresent(m_clientConfig->writeRateLimiter, m_clientConfig->configFactories.writeRateLimiterCreateFn); @@ -91,6 +95,27 @@ void AwsSmithyClientBase::baseCopyInit() { } } +void AwsSmithyClientBase::baseCopyAssign(const AwsSmithyClientBase& other, + std::shared_ptr httpClient, + std::shared_ptr errorMarshaller) { + m_serviceName = other.m_serviceName; + m_serviceUserAgentName = other.m_serviceUserAgentName; + m_httpClient = std::move(httpClient); + m_errorMarshaller = std::move(errorMarshaller); + m_interceptors = Aws::Vector>{Aws::MakeShared("AwsSmithyClientBase")}; + + baseCopyInit(); +} + +void AwsSmithyClientBase::baseMoveAssign(AwsSmithyClientBase&& other) { + m_serviceName = std::move(other.m_serviceName); + m_serviceUserAgentName = std::move(other.m_serviceUserAgentName); + m_httpClient = std::move(other.m_httpClient); + m_errorMarshaller = std::move(other.m_errorMarshaller); + m_interceptors = std::move(other.m_interceptors); + m_userAgentInterceptor = std::move(other.m_userAgentInterceptor); +} + std::shared_ptr AwsSmithyClientBase::BuildHttpRequest(const std::shared_ptr& pRequestCtx, const Aws::Http::URI& uri, diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp index 555b429b432..2b7371e0391 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp @@ -416,4 +416,175 @@ TEST_F(SmithyClientTest, SmithyClientShouldCopyAssignAndMove) { AWS_UNREFERENCED_PARAM(copy); SampleClient moveAssign = std::move(client); AWS_UNREFERENCED_PARAM(assign); +} + +class TestAuthSchemeResolver : public smithy::SigV4AuthSchemeResolver<> +{ +public: + virtual Aws::String GetTestId() const { return "BaseClass"; } +}; + +class TestAuthSchemeResolverDerived : public TestAuthSchemeResolver +{ +public: + TestAuthSchemeResolverDerived(const Aws::String& id):m_testId{id}{} + Aws::String GetTestId() const override { return m_testId; } +protected: + Aws::String m_testId; +}; + +using TestSmithyClientT = smithy::client::AwsSmithyClientT; + +class TestCopyClient : public TestSmithyClientT +{ + public: + TestCopyClient(const MySmithyClientConfig& clientConfig, + const Aws::String& serviceName, + const std::shared_ptr& httpClient, + const std::shared_ptr& errorMarshaller, + const std::shared_ptr endpointProvider, + const std::shared_ptr& authSchemeResolver, + const Aws::UnorderedMap& authSchemesMap): + TestSmithyClientT( + clientConfig, + serviceName, + "ServiceUserAgentName", + httpClient, + errorMarshaller, + endpointProvider, + authSchemeResolver, + authSchemesMap) + {} + + TestCopyClient(const TestCopyClient& other) + : TestSmithyClientT(other) + { + } + TestCopyClient(TestCopyClient&& other) + : TestSmithyClientT(std::move(other)) + { + } + + template + TestCopyClient& operator=(Args&& ... args) + { + TestSmithyClientT::operator=(std::forward(args)...); + return *this; + } + + std::shared_ptr AuthSchemeResolver() + { + return m_authSchemeResolver; + } + + const Aws::String TestGetServiceClientName() const { + return GetServiceClientName(); + } +}; + +TEST_F(SmithyClientTest, TestCopyMove) { + //derived + std::shared_ptr authSchemeResolver = Aws::MakeShared(ALLOCATION_TAG,"DerivedClass"); + //base + std::shared_ptr authSchemeResolver2 = Aws::MakeShared(ALLOCATION_TAG); + + Aws::UnorderedMap authSchemesMap; + + Aws::String key{"aws.auth#sigv4"}; + + //add mock credentials provider for the test to the credentials provider chain + AddCredentialsProvider(Aws::MakeShared("TestCredentialsProviderChain")); + + //create resolver with the credentials provider chain + auto credentialsResolver = Aws::MakeShared(ALLOCATION_TAG, credsProviderChain); + + SigVariant val{smithy::SigV4AuthScheme( credentialsResolver, "MyService", "us-west-2")}; + + authSchemesMap.emplace(key, val); + + // test copy c-tor + { + TestCopyClient toCopy = TestCopyClient( + clientConfig, + "MyServiceCtorCopied", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver, + authSchemesMap); + + TestCopyClient ctorCopied(toCopy); + EXPECT_EQ(ctorCopied.TestGetServiceClientName(), "MyServiceCtorCopied"); + } + + // test copy assign aka operator(const &) + { + TestCopyClient source = TestCopyClient( + clientConfig, + "MyServiceSource", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver, + authSchemesMap); + + TestCopyClient destination = TestCopyClient( + clientConfig, + "MyServiceDestination", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver2, + authSchemesMap); + + destination = source; + EXPECT_EQ(destination.AuthSchemeResolver()->GetTestId(), "DerivedClass"); + EXPECT_EQ(destination.TestGetServiceClientName(), "MyServiceSource"); + } + + // test move c-tor + { + TestCopyClient toMove = TestCopyClient( + clientConfig, + "MyServiceCtorMoved", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver, + authSchemesMap); + + TestCopyClient ctorMoved(std::move(toMove)); + EXPECT_EQ(ctorMoved.TestGetServiceClientName(), "MyServiceCtorMoved"); + } + + // test move assign aka operator=(&&) + { + TestCopyClient toMoveAssign = TestCopyClient( + clientConfig, + "MyServiceMoveAssigned", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver, + authSchemesMap); + + TestCopyClient moveDestination = TestCopyClient( + clientConfig, + "MyServiceMoveDestination", + httpClient, + errorMarshaller, + endPointProvider, + authSchemeResolver2, + authSchemesMap); + + moveDestination = std::move(toMoveAssign); + EXPECT_EQ(moveDestination.TestGetServiceClientName(), "MyServiceMoveAssigned"); + } } \ No newline at end of file diff --git a/tests/aws-cpp-sdk-logs-integration-tests/CloudWatchLogsTests.cpp b/tests/aws-cpp-sdk-logs-integration-tests/CloudWatchLogsTests.cpp index aead872d6c6..f3c14795270 100644 --- a/tests/aws-cpp-sdk-logs-integration-tests/CloudWatchLogsTests.cpp +++ b/tests/aws-cpp-sdk-logs-integration-tests/CloudWatchLogsTests.cpp @@ -59,7 +59,8 @@ namespace ClientConfiguration config; config.scheme = Scheme::HTTPS; config.region = AWS_TEST_REGION; - m_client = Aws::MakeUnique(ALLOCATION_TAG, config); + CloudWatchLogsClient tmpClient(config); // test copy c-tor + m_client = Aws::MakeUnique(ALLOCATION_TAG, tmpClient); CreateLogsGroup(BuildResourceName(BASE_CLOUD_WATCH_LOGS_GROUP)); auto accountId = Aws::Environment::GetEnv("CATAPULT_TEST_ACCOUNT"); diff --git a/tests/aws-cpp-sdk-logs-unit-tests/CMakeLists.txt b/tests/aws-cpp-sdk-logs-unit-tests/CMakeLists.txt new file mode 100644 index 00000000000..d6e0b2bd9b5 --- /dev/null +++ b/tests/aws-cpp-sdk-logs-unit-tests/CMakeLists.txt @@ -0,0 +1,33 @@ +add_project(aws-cpp-sdk-logs-unit-tests + "Unit Tests for the CloudWatch Logs Client" + aws-cpp-sdk-logs + testing-resources + aws_test_main + aws-cpp-sdk-core) + +add_definitions(-DRESOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}/resources") + +if(MSVC AND BUILD_SHARED_LIBS) + add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) +endif() + +enable_testing() + +if(PLATFORM_ANDROID AND BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/CloudWatchLogsUnitTests.cpp) +else() + add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/CloudWatchLogsUnitTests.cpp) +endif() + +set_compiler_flags(${PROJECT_NAME}) +set_compiler_warnings(${PROJECT_NAME}) + +target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS}) + +if(MSVC AND BUILD_SHARED_LIBS) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/DELAYLOAD:aws-cpp-sdk-logs.dll /DELAYLOAD:aws-cpp-sdk-core.dll") + target_link_libraries(${PROJECT_NAME} delayimp.lib) +endif() + +include(GoogleTest) +gtest_add_tests(TARGET ${PROJECT_NAME}) diff --git a/tests/aws-cpp-sdk-logs-unit-tests/CloudWatchLogsUnitTests.cpp b/tests/aws-cpp-sdk-logs-unit-tests/CloudWatchLogsUnitTests.cpp new file mode 100644 index 00000000000..01b27ec6db9 --- /dev/null +++ b/tests/aws-cpp-sdk-logs-unit-tests/CloudWatchLogsUnitTests.cpp @@ -0,0 +1,163 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws::Auth; +using namespace Aws::Http; +using namespace Aws::Client; +using namespace Aws::CloudWatchLogs; +using namespace Aws::CloudWatchLogs::Model; + +const char* LOG_TAG = "CloudWatchLogsTest"; + +class CloudWatchLogsTest : public Aws::Testing::AwsCppSdkGTestSuite { + protected: + std::shared_ptr m_mockHttpClient; + std::shared_ptr m_mockHttpClientFactory; + + void SetUp() { + m_mockHttpClient = Aws::MakeShared(LOG_TAG); + m_mockHttpClientFactory = Aws::MakeShared(LOG_TAG); + m_mockHttpClientFactory->SetClient(m_mockHttpClient); + SetHttpClientFactory(m_mockHttpClientFactory); + } + + void TearDown() { + m_mockHttpClient->Reset(); + m_mockHttpClient = nullptr; + m_mockHttpClientFactory = nullptr; + Aws::Http::CleanupHttp(); + Aws::Http::InitHttp(); + } +}; + +class ClientHolder { + public: + ClientHolder() : client(makeClient()) { + // init client again + client = makeClient(); + } + + PutLogEventsOutcome Foo() { + const auto response = client.PutLogEvents(PutLogEventsRequest{}); + return response; + } + + const CloudWatchLogsClient& getClient() { return client; } + + CloudWatchLogsClient&& moveClient() { return std::move(client); } + + private: + CloudWatchLogsClient makeClient() { + Aws::Client::ClientConfigurationInitValues cfgInit; + cfgInit.shouldDisableIMDS = true; + Aws::Client::ClientConfiguration clientConfig(cfgInit); + clientConfig.region = Aws::Region::US_EAST_1; + AWSCredentials credentials{"mock", "credentials"}; + return CloudWatchLogsClient(credentials, clientConfig); + } + + CloudWatchLogsClient client; +}; + +TEST_F(CloudWatchLogsTest, ClientOperatorCopyMoveEquals) { + auto mockResponse = [this]() { + std::shared_ptr requestTmp = + CreateHttpRequest(Aws::Http::URI("dummy"), Aws::Http::HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); + auto successResponse = Aws::MakeShared(LOG_TAG, requestTmp); + successResponse->SetResponseCode(HttpResponseCode::OK); + successResponse->GetResponseBody() << "{}"; + m_mockHttpClient->AddResponseToReturn(successResponse); + }; + + // Check typical user wrapper + mockResponse(); + ClientHolder wrapped{}; + auto resWrapped = wrapped.Foo(); + AWS_ASSERT_SUCCESS(resWrapped); + + // check copy constructed + { + mockResponse(); + CloudWatchLogsClient copied(wrapped.getClient()); + auto resCopied = copied.PutLogEvents(PutLogEventsRequest{}); + AWS_ASSERT_SUCCESS(resCopied); + } + + // check that the original is still alive + { + mockResponse(); + auto resWrappedAfterCopy = wrapped.Foo(); + AWS_ASSERT_SUCCESS(resWrappedAfterCopy); + } + + // check move construction + { + mockResponse(); + CloudWatchLogsClient moved(std::move(wrapped.moveClient())); + auto resMoved = moved.PutLogEvents(PutLogEventsRequest{}); + AWS_ASSERT_SUCCESS(resMoved); + } + + // check that original is "unspecified but valid state" after move + { + mockResponse(); + auto resWrappedAfterMove = wrapped.Foo(); + // move is actually redirected to copy because the generated client does not generate move c-tor, so it results in a copy + AWS_ASSERT_SUCCESS(resWrappedAfterMove); + } + + ClientHolder anotherWrapper{}; + // operator=(const &) + { + Aws::Client::ClientConfigurationInitValues cfgInit; + cfgInit.shouldDisableIMDS = true; + Aws::Client::ClientConfiguration clientConfig(cfgInit); + CloudWatchLogsClient copyAssigned(clientConfig); + + mockResponse(); + copyAssigned = anotherWrapper.getClient(); + auto resCopyAssigned = copyAssigned.PutLogEvents(PutLogEventsRequest{}); + AWS_ASSERT_SUCCESS(resCopyAssigned); + } + + // check that the original is still alive + { + mockResponse(); + auto resWrappedAfterCopyAssign = anotherWrapper.Foo(); + AWS_ASSERT_SUCCESS(resWrappedAfterCopyAssign); + } + + // operator=(&&) + { + Aws::Client::ClientConfigurationInitValues cfgInit; + cfgInit.shouldDisableIMDS = true; + Aws::Client::ClientConfiguration clientConfig(cfgInit); + CloudWatchLogsClient moveAssigned(clientConfig); + + mockResponse(); + moveAssigned = std::move(anotherWrapper.moveClient()); + auto resMoveAssigned = moveAssigned.PutLogEvents(PutLogEventsRequest{}); + AWS_ASSERT_SUCCESS(resMoveAssigned); + } + + // check that original is "unspecified but valid state" after move + { + mockResponse(); + auto resWrappedAfterMoveAssign = anotherWrapper.Foo(); + // move is actually redirected to copy because the generated client does not generate operator(&&), so it results in a copy + AWS_ASSERT_SUCCESS(resWrappedAfterMoveAssign); + } +} \ No newline at end of file diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm index 9a7b9187ad4..0614f8d3d88 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm @@ -110,15 +110,15 @@ namespace ${rootNamespace} #if($serviceModel.enableVirtualOperations) #set($finalClass = "") #end - class ${CppViewHelper.computeExportValue($metadata.classNamePrefix)} ${className}${finalClass} : public smithy::client::AwsSmithyClientT<${rootNamespace}::${serviceNamespace}::SERVICE_NAME, - ${serviceConfiguration}, - smithy::${AuthSchemeResolver}<${metadata.classNamePrefix}EndpointProvider, ${serviceConfiguration}>, - ${rootNamespace}::Crt::Variant<${AuthSchemeVariants}>, - ${metadata.classNamePrefix}EndpointProviderBase, - smithy::client::$serializer, - smithy::client::$serializerOutcome, - Aws::Client::${metadata.classNamePrefix}ErrorMarshaller>, - Aws::Client::ClientWithAsyncTemplateMethods<${className}> + class ${CppViewHelper.computeExportValue($metadata.classNamePrefix)} ${className}${finalClass} : Aws::Client::ClientWithAsyncTemplateMethods<${className}>, + public smithy::client::AwsSmithyClientT<${rootNamespace}::${serviceNamespace}::SERVICE_NAME, + ${serviceConfiguration}, + smithy::${AuthSchemeResolver}<${metadata.classNamePrefix}EndpointProvider, ${serviceConfiguration}>, + ${rootNamespace}::Crt::Variant<${AuthSchemeVariants}>, + ${metadata.classNamePrefix}EndpointProviderBase, + smithy::client::$serializer, + smithy::client::$serializerOutcome, + Aws::Client::${metadata.classNamePrefix}ErrorMarshaller>, { public: static const char* GetServiceName(); diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/smithy/SmithyClientHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/smithy/SmithyClientHeader.vm index 79f5e9e3914..ed067552b39 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/smithy/SmithyClientHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/smithy/SmithyClientHeader.vm @@ -42,15 +42,15 @@ namespace ${serviceNamespace} #if($serviceModel.enableVirtualOperations) #set($finalClass = "") #end - class ${CppViewHelper.computeExportValue($metadata.classNamePrefix)} ${className}${finalClass} : smithy::client::AwsSmithyClientT<${rootNamespace}::${serviceNamespace}::SERVICE_NAME, + class ${CppViewHelper.computeExportValue($metadata.classNamePrefix)} ${className}${finalClass} : Aws::Client::ClientWithAsyncTemplateMethods<${className}>, + smithy::client::AwsSmithyClientT<${rootNamespace}::${serviceNamespace}::SERVICE_NAME, ${serviceConfiguration}, smithy::${AuthSchemeResolver}<>, ${rootNamespace}::Crt::Variant<${AuthSchemeVariants}>, ${metadata.classNamePrefix}EndpointProviderBase, smithy::client::$serializer, smithy::client::$serializerOutcome, - Aws::Client::${metadata.classNamePrefix}ErrorMarshaller>, - Aws::Client::ClientWithAsyncTemplateMethods<${className}> + Aws::Client::${metadata.classNamePrefix}ErrorMarshaller> { public: static const char* GetServiceName();