Skip to content

Commit e1e3344

Browse files
committed
use decorator class for query compatible
1 parent 819b765 commit e1e3344

File tree

9 files changed

+148
-150
lines changed

9 files changed

+148
-150
lines changed

generated/src/aws-cpp-sdk-sqs/include/aws/sqs/SQSErrorMarshaller.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Aws
1313
namespace Client
1414
{
1515

16-
class AWS_SQS_API SQSErrorMarshaller : public Aws::Client::JsonErrorMarshaller
16+
class AWS_SQS_API SQSErrorMarshaller : public Aws::Client::JsonErrorMarshallerQueryCompatible
1717
{
1818
public:
1919
Aws::Client::AWSError<Aws::Client::CoreErrors> FindErrorByName(const char* exceptionName) const override;

generated/src/aws-cpp-sdk-sqs/include/aws/sqs/SQSRequest.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace SQS
3131
if(headers.size() == 0 || (headers.size() > 0 && headers.count(Aws::Http::CONTENT_TYPE_HEADER) == 0))
3232
{
3333
headers.emplace(Aws::Http::HeaderValuePair(Aws::Http::CONTENT_TYPE_HEADER, Aws::AMZN_JSON_CONTENT_TYPE_1_0 ));
34+
headers.emplace(Aws::Http::HeaderValuePair(Aws::Http::X_AMZN_QUERY_MODE,"true"));
3435
}
3536
headers.emplace(Aws::Http::HeaderValuePair(Aws::Http::API_VERSION_HEADER, "2012-11-05"));
3637
return headers;

src/aws-cpp-sdk-core/include/aws/core/client/AWSError.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace Aws
4343
// Allow ErrorMarshaller to set error payload.
4444
friend class XmlErrorMarshaller;
4545
friend class JsonErrorMarshaller;
46+
friend class JsonErrorMarshallerQueryCompatible;
4647
template<typename T> friend class AWSError;
4748
public:
4849
/**

src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,29 @@ namespace Aws
8080
{
8181
using AWSErrorMarshaller::Marshall;
8282
public:
83-
JsonErrorMarshaller(bool queryCompatibilityMode);
84-
JsonErrorMarshaller() = default;
8583
/**
8684
* Converts an exceptionName and message into an Error object, if it
8785
* can be parsed. Otherwise, it returns and AWSError with
8886
* CoreErrors::UNKNOWN as the error type.
8987
*/
90-
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;
88+
virtual AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;
9189

9290
AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const override;
9391

9492
protected:
9593
const Aws::Utils::Json::JsonValue& GetJsonPayloadFromError(const AWSError<CoreErrors>&) const;
96-
bool isQueryCompatibleMode() const;
97-
const bool m_queryCompatibilityMode{false};
94+
};
95+
96+
class AWS_CORE_API JsonErrorMarshallerQueryCompatible : public JsonErrorMarshaller {
97+
using AWSErrorMarshaller::Marshall;
98+
99+
public:
100+
/**
101+
* Converts an exceptionName and message into an Error object, if it
102+
* can be parsed. Otherwise, it returns and AWSError with
103+
* CoreErrors::UNKNOWN as the error type.
104+
*/
105+
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;
98106
};
99107

100108
class AWS_CORE_API XmlErrorMarshaller : public AWSErrorMarshaller {

src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp

Lines changed: 125 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ static CoreErrors GuessBodylessErrorType(const Aws::Http::HttpResponseCode respo
4040
}
4141
}
4242

43-
JsonErrorMarshaller::JsonErrorMarshaller(bool queryCompatibilityMode)
44-
: AWSErrorMarshaller(), m_queryCompatibilityMode{queryCompatibilityMode} {}
45-
46-
bool JsonErrorMarshaller::isQueryCompatibleMode() const { return m_queryCompatibilityMode; }
47-
4843
AWSError<CoreErrors> JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const {
4944
Aws::StringStream memoryStream;
5045
std::copy(std::istreambuf_iterator<char>(httpResponse.GetResponseBody()), std::istreambuf_iterator<char>(),
@@ -70,46 +65,6 @@ AWSError<CoreErrors> JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse
7065
error.SetMessage(message);
7166
}
7267

73-
if (isQueryCompatibleMode() && !error.GetExceptionName().empty()) {
74-
/*
75-
AWS Query-Compatible mode: This is a special setting that allows
76-
certain AWS services to communicate using a specific "query"
77-
format, which can send customized error codes. Users are divided
78-
into different groups based on how they communicate with the
79-
service: Group #1: Users using the AWS Query format, receiving
80-
custom error codes. Group #2: Users using the regular AWS JSON
81-
format without the trait, receiving standard error codes. Group #3:
82-
Users using the AWS JSON format with the trait, receiving custom
83-
error codes.
84-
85-
The header "x-amzn-query-error" shouldn't be present if it's not
86-
awsQueryCompatible, so added checks for it.
87-
*/
88-
89-
if (httpResponse.HasHeader(QUERY_ERROR_HEADER)) {
90-
auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER);
91-
auto locationOfSemicolon = errorCodeString.find_first_of(';');
92-
Aws::String errorCode;
93-
94-
if (locationOfSemicolon != Aws::String::npos) {
95-
errorCode = errorCodeString.substr(0, locationOfSemicolon);
96-
} else {
97-
errorCode = errorCodeString;
98-
}
99-
100-
error.SetExceptionName(errorCode);
101-
}
102-
// check for exception name from payload field 'type'
103-
else if (payloadView.ValueExists(TYPE)) {
104-
// handle missing header and parse code from message
105-
const auto& typeStr = payloadView.GetString(TYPE);
106-
auto locationOfPound = typeStr.find_first_of('#');
107-
if (locationOfPound != Aws::String::npos) {
108-
error.SetExceptionName(typeStr.substr(locationOfPound + 1));
109-
}
110-
}
111-
}
112-
11368
} else {
11469
bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode());
11570
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG,
@@ -146,67 +101,60 @@ AWSError<CoreErrors> JsonErrorMarshaller::BuildAWSError(const std::shared_ptr<Ht
146101
return error;
147102
}
148103

149-
const JsonValue& JsonErrorMarshaller::GetJsonPayloadFromError(const AWSError<CoreErrors>& error) const
150-
{
151-
return error.GetJsonPayload();
152-
}
104+
const JsonValue& JsonErrorMarshaller::GetJsonPayloadFromError(const AWSError<CoreErrors>& error) const { return error.GetJsonPayload(); }
153105

154-
AWSError<CoreErrors> XmlErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const
155-
{
156-
XmlDocument doc = XmlDocument::CreateFromXmlStream(httpResponse.GetResponseBody());
157-
AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << doc.ConvertToString());
158-
bool errorParsed = false;
159-
AWSError<CoreErrors> error;
160-
if (doc.WasParseSuccessful())
161-
{
162-
XmlNode errorNode = doc.GetRootElement();
106+
AWSError<CoreErrors> XmlErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const {
107+
XmlDocument doc = XmlDocument::CreateFromXmlStream(httpResponse.GetResponseBody());
108+
AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << doc.ConvertToString());
109+
bool errorParsed = false;
110+
AWSError<CoreErrors> error;
111+
if (doc.WasParseSuccessful()) {
112+
XmlNode errorNode = doc.GetRootElement();
163113

164-
Aws::String requestId(!errorNode.FirstChild("RequestId").IsNull() ? errorNode.FirstChild("RequestId").GetText() :
165-
!errorNode.FirstChild("RequestID").IsNull() ? errorNode.FirstChild("RequestID").GetText() : "");
114+
Aws::String requestId(!errorNode.FirstChild("RequestId").IsNull() ? errorNode.FirstChild("RequestId").GetText()
115+
: !errorNode.FirstChild("RequestID").IsNull() ? errorNode.FirstChild("RequestID").GetText()
116+
: "");
166117

167-
if (errorNode.GetName() != "Error")
168-
{
169-
errorNode = doc.GetRootElement().FirstChild("Error");
170-
}
171-
if (errorNode.IsNull())
172-
{
173-
errorNode = doc.GetRootElement().FirstChild("Errors");
174-
if(!errorNode.IsNull())
175-
{
176-
errorNode = errorNode.FirstChild("Error");
177-
}
178-
}
118+
if (errorNode.GetName() != "Error") {
119+
errorNode = doc.GetRootElement().FirstChild("Error");
120+
}
121+
if (errorNode.IsNull()) {
122+
errorNode = doc.GetRootElement().FirstChild("Errors");
123+
if (!errorNode.IsNull()) {
124+
errorNode = errorNode.FirstChild("Error");
125+
}
126+
}
179127

180-
if (!errorNode.IsNull())
181-
{
182-
requestId = !requestId.empty() ? requestId : !errorNode.FirstChild("RequestId").IsNull() ? errorNode.FirstChild("RequestId").GetText() :
183-
!errorNode.FirstChild("RequestID").IsNull() ? errorNode.FirstChild("RequestID").GetText() : "";
184-
185-
XmlNode codeNode = errorNode.FirstChild("Code");
186-
XmlNode messageNode = errorNode.FirstChild("Message");
187-
188-
if (!codeNode.IsNull())
189-
{
190-
error = Marshall(StringUtils::Trim(codeNode.GetText().c_str()),
191-
StringUtils::Trim(messageNode.GetText().c_str()));
192-
errorParsed = true;
193-
}
194-
}
128+
if (!errorNode.IsNull()) {
129+
requestId = !requestId.empty() ? requestId
130+
: !errorNode.FirstChild("RequestId").IsNull() ? errorNode.FirstChild("RequestId").GetText()
131+
: !errorNode.FirstChild("RequestID").IsNull() ? errorNode.FirstChild("RequestID").GetText()
132+
: "";
195133

196-
error.SetRequestId(requestId);
197-
}
134+
XmlNode codeNode = errorNode.FirstChild("Code");
135+
XmlNode messageNode = errorNode.FirstChild("Message");
198136

199-
if(!errorParsed)
200-
{
201-
// An error occurred attempting to parse the httpResponse as an XML stream, so we're just
202-
// going to dump the XML parsing error and the http response code as a string
203-
AWS_LOGSTREAM_WARN(AWS_ERROR_MARSHALLER_LOG_TAG, "Unable to generate a proper httpResponse from the response "
204-
"stream. Response code: " << static_cast< uint32_t >(httpResponse.GetResponseCode()));
205-
error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
137+
if (!codeNode.IsNull()) {
138+
error = Marshall(StringUtils::Trim(codeNode.GetText().c_str()), StringUtils::Trim(messageNode.GetText().c_str()));
139+
errorParsed = true;
140+
}
206141
}
207142

208-
error.SetXmlPayload(std::move(doc));
209-
return error;
143+
error.SetRequestId(requestId);
144+
}
145+
146+
if (!errorParsed) {
147+
// An error occurred attempting to parse the httpResponse as an XML stream, so we're just
148+
// going to dump the XML parsing error and the http response code as a string
149+
AWS_LOGSTREAM_WARN(AWS_ERROR_MARSHALLER_LOG_TAG,
150+
"Unable to generate a proper httpResponse from the response "
151+
"stream. Response code: "
152+
<< static_cast<uint32_t>(httpResponse.GetResponseCode()));
153+
error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
154+
}
155+
156+
error.SetXmlPayload(std::move(doc));
157+
return error;
210158
}
211159

212160
AWSError<CoreErrors> XmlErrorMarshaller::BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const
@@ -300,3 +248,82 @@ AWSError<CoreErrors> AWSErrorMarshaller::FindErrorByHttpResponseCode(Aws::Http::
300248
{
301249
return CoreErrorsMapper::GetErrorForHttpResponseCode(code);
302250
}
251+
252+
253+
AWSError<CoreErrors> JsonErrorMarshallerQueryCompatible::Marshall(const Aws::Http::HttpResponse& httpResponse) const
254+
{
255+
Aws::StringStream memoryStream;
256+
std::copy(std::istreambuf_iterator<char>(httpResponse.GetResponseBody()), std::istreambuf_iterator<char>(),
257+
std::ostreambuf_iterator<char>(memoryStream));
258+
Aws::String rawPayloadStr = memoryStream.str();
259+
260+
JsonValue exceptionPayload(rawPayloadStr);
261+
JsonView payloadView(exceptionPayload);
262+
AWSError<CoreErrors> error;
263+
if (exceptionPayload.WasParseSuccessful()) {
264+
AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable());
265+
266+
Aws::String message(payloadView.ValueExists(MESSAGE_CAMEL_CASE) ? payloadView.GetString(MESSAGE_CAMEL_CASE)
267+
: payloadView.ValueExists(MESSAGE_LOWER_CASE) ? payloadView.GetString(MESSAGE_LOWER_CASE)
268+
: "");
269+
270+
if (httpResponse.HasHeader(ERROR_TYPE_HEADER)) {
271+
error = Marshall(httpResponse.GetHeader(ERROR_TYPE_HEADER), message);
272+
} else if (payloadView.ValueExists(TYPE)) {
273+
error = Marshall(payloadView.GetString(TYPE), message);
274+
} else {
275+
error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
276+
error.SetMessage(message);
277+
}
278+
279+
if (!error.GetExceptionName().empty()) {
280+
/*
281+
AWS Query-Compatible mode: This is a special setting that allows
282+
certain AWS services to communicate using a specific "query"
283+
format, which can send customized error codes. Users are divided
284+
into different groups based on how they communicate with the
285+
service: Group #1: Users using the AWS Query format, receiving
286+
custom error codes. Group #2: Users using the regular AWS JSON
287+
format without the trait, receiving standard error codes. Group #3:
288+
Users using the AWS JSON format with the trait, receiving custom
289+
error codes.
290+
291+
The header "x-amzn-query-error" shouldn't be present if it's not
292+
awsQueryCompatible, so added checks for it.
293+
*/
294+
295+
if (httpResponse.HasHeader(QUERY_ERROR_HEADER)) {
296+
auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER);
297+
auto locationOfSemicolon = errorCodeString.find_first_of(';');
298+
Aws::String errorCode;
299+
300+
if (locationOfSemicolon != Aws::String::npos) {
301+
errorCode = errorCodeString.substr(0, locationOfSemicolon);
302+
} else {
303+
errorCode = errorCodeString;
304+
}
305+
306+
error.SetExceptionName(errorCode);
307+
}
308+
// check for exception name from payload field 'type'
309+
else if (payloadView.ValueExists(TYPE)) {
310+
// handle missing header and parse code from message
311+
const auto& typeStr = payloadView.GetString(TYPE);
312+
auto locationOfPound = typeStr.find_first_of('#');
313+
if (locationOfPound != Aws::String::npos) {
314+
error.SetExceptionName(typeStr.substr(locationOfPound + 1));
315+
}
316+
}
317+
}
318+
319+
} else {
320+
bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode());
321+
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG,
322+
"Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << rawPayloadStr);
323+
error = AWSError<CoreErrors>(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + rawPayloadStr, isRetryable);
324+
}
325+
326+
error.SetRequestId(httpResponse.HasHeader(REQUEST_ID_HEADER) ? httpResponse.GetHeader(REQUEST_ID_HEADER) : "");
327+
error.SetJsonPayload(std::move(exceptionPayload));
328+
return error;
329+
}

tests/aws-cpp-sdk-core-tests/aws/client/AWSErrorMashallerTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ TEST_F(AWSErrorMarshallerTest, TestErrorsWithPrefixParse)
474474
ASSERT_EQ(requestId, error.GetRequestId());
475475
ASSERT_FALSE(error.ShouldRetry());
476476

477-
JsonErrorMarshaller awsErrorMarshaller2(true);
477+
JsonErrorMarshallerQueryCompatible awsErrorMarshaller2;
478478
error = awsErrorMarshaller2.Marshall(
479479
*BuildHttpResponse(exceptionPrefix + "AccessDeniedException", message, requestId, LowerCaseMessage, "AwsQueryErrorCode"));
480480
ASSERT_EQ(CoreErrors::ACCESS_DENIED, error.GetErrorType());
@@ -743,7 +743,7 @@ TEST_F(AWSErrorMarshallerTest, TestErrorsWithoutPrefixParse)
743743
ASSERT_EQ(requestId, error.GetRequestId());
744744
ASSERT_FALSE(error.ShouldRetry());
745745

746-
JsonErrorMarshaller awsErrorMarshaller2(true);
746+
JsonErrorMarshallerQueryCompatible awsErrorMarshaller2;
747747
error = awsErrorMarshaller2.Marshall(
748748
*BuildHttpResponse(exceptionPrefix + "AccessDeniedException", message, requestId, LowerCaseMessage, "AwsQueryErrorCode"));
749749
ASSERT_EQ(CoreErrors::ACCESS_DENIED, error.GetErrorType());

0 commit comments

Comments
 (0)