-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create interfaces for smithy interceptors
- Loading branch information
Showing
5 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/aws-cpp-sdk-core/include/smithy/interceptor/Interceptor.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0. | ||
*/ | ||
#pragma once | ||
#include <smithy/interceptor/InterceptorContext.h> | ||
|
||
namespace smithy | ||
{ | ||
namespace interceptor | ||
{ | ||
class Interceptor | ||
{ | ||
public: | ||
virtual ~Interceptor() = default; | ||
|
||
using ModifyRequestOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpRequest>, Aws::Client::AWSError<Aws::Client::CoreErrors>>; | ||
virtual ModifyRequestOutcome ModifyRequest(InterceptorContext& context) = 0; | ||
|
||
using ModifyResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, Aws::Client::AWSError<Aws::Client::CoreErrors>>; | ||
virtual ModifyResponseOutcome ModifyResponse(InterceptorContext& context) = 0; | ||
}; | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
src/aws-cpp-sdk-core/include/smithy/interceptor/InterceptorContext.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/** | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0. | ||
*/ | ||
#pragma once | ||
#include <aws/core/utils/memory/stl/AWSString.h> | ||
#include <aws/core/utils/Outcome.h> | ||
#include <aws/core/http/HttpRequest.h> | ||
#include <aws/core/http/HttpResponse.h> | ||
#include <aws/core/client/AWSError.h> | ||
#include <aws/core/client/CoreErrors.h> | ||
|
||
namespace smithy | ||
{ | ||
namespace interceptor | ||
{ | ||
class InterceptorContext | ||
{ | ||
public: | ||
InterceptorContext() = default; | ||
virtual ~InterceptorContext() = default; | ||
InterceptorContext(const InterceptorContext& other) = delete; | ||
InterceptorContext(InterceptorContext&& other) noexcept = default; | ||
InterceptorContext& operator=(const InterceptorContext& other) = delete; | ||
InterceptorContext& operator=(InterceptorContext&& other) noexcept = default; | ||
|
||
using GetRequestOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpRequest>, Aws::Client::AWSError<Aws::Client::CoreErrors>>; | ||
GetRequestOutcome GetRequest() const | ||
{ | ||
if (!m_request) | ||
{ | ||
return Aws::Client::AWSError<Aws::Client::CoreErrors>{ | ||
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND, | ||
"ResourceNotFoundException", | ||
"Request is NULL", | ||
false | ||
}; | ||
} | ||
return m_request; | ||
} | ||
|
||
void SetRequest(const std::shared_ptr<Aws::Http::HttpRequest>& request) | ||
{ | ||
this->m_request = request; | ||
} | ||
|
||
using GetResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, Aws::Client::AWSError<Aws::Client::CoreErrors>>; | ||
GetResponseOutcome GetResponse() const | ||
{ | ||
if (!m_response) | ||
{ | ||
return Aws::Client::AWSError<Aws::Client::CoreErrors>{ | ||
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND, | ||
"ResourceNotFoundException", | ||
"Response is NULL", | ||
false | ||
}; | ||
} | ||
return m_response; | ||
} | ||
|
||
void SetResponse(const std::shared_ptr<Aws::Http::HttpResponse>& response) | ||
{ | ||
this->m_response = response; | ||
} | ||
|
||
using GetAttributeOutcome = Aws::Utils::Outcome<Aws::String, Aws::Client::AWSError<Aws::Client::CoreErrors>>; | ||
GetAttributeOutcome GetAttribute(const Aws::String& attribute) | ||
{ | ||
const auto attribute_iter = m_attributes.find(attribute); | ||
if (attribute_iter == m_attributes.end()) | ||
{ | ||
return Aws::Client::AWSError<Aws::Client::CoreErrors>{ | ||
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND, | ||
"ResourceNotFoundException", | ||
"Attribute not found", | ||
false | ||
}; | ||
} | ||
return attribute_iter->second; | ||
} | ||
|
||
void SetAttribute(const Aws::String& attribute, const Aws::String& value) | ||
{ | ||
m_attributes.emplace(attribute, value); | ||
} | ||
|
||
private: | ||
Aws::Map<Aws::String, Aws::String> m_attributes{}; | ||
std::shared_ptr<Aws::Http::HttpRequest> m_request{nullptr}; | ||
std::shared_ptr<Aws::Http::HttpResponse> m_response{nullptr}; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
tests/aws-cpp-sdk-core-tests/smithy/interceptor/InterceptorTest.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/** | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0. | ||
*/ | ||
#include <aws/testing/AwsCppSdkGTestSuite.h> | ||
#include <aws/core/http/standard/StandardHttpResponse.h> | ||
#include <smithy/interceptor/InterceptorContext.h> | ||
#include <smithy/interceptor/Interceptor.h> | ||
|
||
using namespace smithy::interceptor; | ||
using namespace Aws; | ||
using namespace Aws::Client; | ||
using namespace Aws::Http; | ||
using namespace Aws::Http::Standard; | ||
using namespace Aws::Utils; | ||
using namespace Aws::Utils::Stream; | ||
using namespace Aws::Testing; | ||
|
||
class SmithyInterceptorTest : public AwsCppSdkGTestSuite | ||
{ | ||
}; | ||
|
||
class MockSuccessInterceptor : public Interceptor | ||
{ | ||
public: | ||
MockSuccessInterceptor() = default; | ||
~MockSuccessInterceptor() override = default; | ||
|
||
ModifyRequestOutcome ModifyRequest(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorRequest", "Called"); | ||
return context.GetRequest(); | ||
} | ||
|
||
ModifyResponseOutcome ModifyResponse(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorResponse", "Called"); | ||
return context.GetResponse(); | ||
} | ||
}; | ||
|
||
class MockRequestFailureInterceptor : public Interceptor | ||
{ | ||
public: | ||
MockRequestFailureInterceptor() = default; | ||
~MockRequestFailureInterceptor() override = default; | ||
|
||
ModifyRequestOutcome ModifyRequest(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorRequest", "Called"); | ||
return Aws::Client::AWSError<CoreErrors>{ | ||
CoreErrors::VALIDATION, | ||
"RequestInterceptorException", | ||
"The request interceptor failed", | ||
false | ||
};; | ||
} | ||
|
||
ModifyResponseOutcome ModifyResponse(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorResponse", "Called"); | ||
return context.GetResponse(); | ||
} | ||
}; | ||
|
||
class MockResponseFailureInterceptor : public Interceptor | ||
{ | ||
public: | ||
MockResponseFailureInterceptor() = default; | ||
~MockResponseFailureInterceptor() override = default; | ||
|
||
ModifyRequestOutcome ModifyRequest(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorRequest", "Called"); | ||
return context.GetRequest(); | ||
} | ||
|
||
ModifyResponseOutcome ModifyResponse(InterceptorContext& context) override | ||
{ | ||
context.SetAttribute("MockInterceptorResponse", "Called"); | ||
return Aws::Client::AWSError<CoreErrors>{ | ||
CoreErrors::VALIDATION, | ||
"ResponseInterceptorException", | ||
"The response interceptor failed", | ||
false | ||
};; | ||
} | ||
}; | ||
|
||
class MockClient | ||
{ | ||
public: | ||
MockClient() = delete; | ||
MockClient(const MockClient& other) = delete; | ||
MockClient(MockClient&& other) noexcept = default; | ||
MockClient& operator=(const MockClient& other) = delete; | ||
MockClient& operator=(MockClient&& other) noexcept = default; | ||
|
||
static MockClient MakeClient(Aws::UniquePtr<Interceptor> interceptor) | ||
{ | ||
Vector<UniquePtr<Interceptor>> interceptors; | ||
interceptors.emplace_back(std::move(interceptor)); | ||
return MockClient{std::move(interceptors)}; | ||
} | ||
|
||
using RequestOutcome = Outcome<std::shared_ptr<HttpResponse>, AWSError<CoreErrors>>; | ||
RequestOutcome MakeRequest(const std::shared_ptr<HttpRequest>& request, InterceptorContext& context) const | ||
{ | ||
context.SetRequest(request); | ||
for (const auto& interceptor: m_interceptors) | ||
{ | ||
const auto modifiedRequest = interceptor->ModifyRequest(context); | ||
if (!modifiedRequest.IsSuccess()) | ||
{ | ||
return modifiedRequest.GetError(); | ||
} | ||
} | ||
auto response = Aws::MakeShared<StandardHttpResponse>("SmithyInterceptorTest", request); | ||
context.SetResponse(response); | ||
for (const auto& interceptor: m_interceptors) | ||
{ | ||
const auto modifiedResponse = interceptor->ModifyResponse(context); | ||
if (!modifiedResponse.IsSuccess()) | ||
{ | ||
return modifiedResponse.GetError(); | ||
} | ||
} | ||
return context.GetResponse(); | ||
} | ||
|
||
private: | ||
explicit MockClient(Vector<UniquePtr<Interceptor>> interceptors) | ||
: m_interceptors(std::move(interceptors)) | ||
{ | ||
} | ||
|
||
Vector<UniquePtr<Interceptor>> m_interceptors{}; | ||
}; | ||
|
||
TEST_F(SmithyInterceptorTest, MockInterceptorShouldReturnSuccess) | ||
{ | ||
const auto uri = "https://www.villagepsychic.net/"; | ||
auto request = CreateHttpRequest(URI{uri}, HttpMethod::HTTP_GET, DefaultResponseStreamFactoryMethod); | ||
auto interceptor = Aws::MakeUnique<MockSuccessInterceptor>("SmithyInterceptorTest"); | ||
const auto client = MockClient::MakeClient(std::move(interceptor)); | ||
InterceptorContext context{}; | ||
const auto response = client.MakeRequest(request, context); | ||
EXPECT_TRUE(response.IsSuccess()); | ||
EXPECT_TRUE(context.GetAttribute("MockInterceptorRequest").IsSuccess()); | ||
EXPECT_TRUE(context.GetAttribute("MockInterceptorResponse").IsSuccess()); | ||
} | ||
|
||
TEST_F(SmithyInterceptorTest, MockInterceptorShouldReturnFailureRequset) | ||
{ | ||
const auto uri = "https://www.villagepsychic.net/"; | ||
auto request = CreateHttpRequest(URI{uri}, HttpMethod::HTTP_GET, DefaultResponseStreamFactoryMethod); | ||
auto interceptor = Aws::MakeUnique<MockRequestFailureInterceptor>("SmithyInterceptorTest"); | ||
const auto client = MockClient::MakeClient(std::move(interceptor)); | ||
InterceptorContext context{}; | ||
const auto response = client.MakeRequest(request, context); | ||
EXPECT_FALSE(response.IsSuccess()); | ||
EXPECT_TRUE(context.GetAttribute("MockInterceptorRequest").IsSuccess()); | ||
EXPECT_FALSE(context.GetAttribute("MockInterceptorResponse").IsSuccess()); | ||
} | ||
|
||
TEST_F(SmithyInterceptorTest, MockInterceptorShouldReturnFailureReseponse) | ||
{ | ||
const auto uri = "https://www.villagepsychic.net/"; | ||
auto request = CreateHttpRequest(URI{uri}, HttpMethod::HTTP_GET, DefaultResponseStreamFactoryMethod); | ||
auto interceptor = Aws::MakeUnique<MockResponseFailureInterceptor>("SmithyInterceptorTest"); | ||
const auto client = MockClient::MakeClient(std::move(interceptor)); | ||
InterceptorContext context{}; | ||
const auto response = client.MakeRequest(request, context); | ||
EXPECT_FALSE(response.IsSuccess()); | ||
EXPECT_TRUE(context.GetAttribute("MockInterceptorRequest").IsSuccess()); | ||
EXPECT_TRUE(context.GetAttribute("MockInterceptorResponse").IsSuccess()); | ||
} |