Skip to content

Commit

Permalink
Modify SDK to not fail on invalid Expires header (#5056)
Browse files Browse the repository at this point in the history
* Add Warnings in the async client interface and CrtClient against passing in Runnable::run

* Update warning message

* Updated warning message

* Added paragraph notation

* Add a paragraph notation

* Modify S3 expires field to not fail on parsing errors and instead, log warnings. Also modified the Expires field to be a timestamp

* Fix checkstyle issues

* Fixed duplicates in customization.config definition

* Remove duplicate definitions

* Facilitate custom handling of data type conversion errors

* Removed test class

* Remove incorrectly added statements

* Added Unit tests

* Changed to Junit5

* Fixed checkstyle issues

* Added wiremock

* assertThat instead of assertTrue and logged the entire exception stack

* modified error message
  • Loading branch information
anirudh9391 committed Apr 30, 2024
1 parent e74fa31 commit 31ec0e7
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 7 deletions.
Expand Up @@ -160,6 +160,11 @@ private void postprocessModifyMemberProperty(ShapeModel shapeModel, String membe
.getUnmarshallLocationName());
}

if (modifyModel.isIgnoreDataTypeConversionFailures()) {
MemberModel memberModel = shapeModel.findMemberModelByC2jName(memberName);
memberModel.ignoreDataTypeConversionFailures(true);
}

}

/**
Expand Down
Expand Up @@ -59,6 +59,11 @@ public class ModifyModelShapeModifier {

private String unmarshallLocationName;

/**
* Indicates whether data type conversion failures are to be ignored
*/
private boolean ignoreDataTypeConversionFailures;

public String getDeprecatedMessage() {
return deprecatedMessage;
}
Expand Down Expand Up @@ -130,4 +135,12 @@ public String getEmitAsType() {
public void setEmitAsType(String emitAsType) {
this.emitAsType = emitAsType;
}

public void setIgnoreDataTypeConversionFailures(boolean ignoreDataTypeConversionFailures) {
this.ignoreDataTypeConversionFailures = ignoreDataTypeConversionFailures;
}

public boolean isIgnoreDataTypeConversionFailures() {
return ignoreDataTypeConversionFailures;
}
}
Expand Up @@ -115,6 +115,8 @@ public class MemberModel extends DocumentationModel {

private ContextParam contextParam;

private boolean ignoreDataTypeConversionFailures;

public String getName() {
return name;
}
Expand Down Expand Up @@ -775,4 +777,12 @@ public ContextParam getContextParam() {
public void setContextParam(ContextParam contextParam) {
this.contextParam = contextParam;
}

public void ignoreDataTypeConversionFailures(boolean ignoreDataTypeConversionFailures) {
this.ignoreDataTypeConversionFailures = ignoreDataTypeConversionFailures;
}

public boolean ignoreDataTypeConversionFailures() {
return ignoreDataTypeConversionFailures;
}
}
Expand Up @@ -39,6 +39,7 @@
import software.amazon.awssdk.core.SdkField;
import software.amazon.awssdk.core.protocol.MarshallLocation;
import software.amazon.awssdk.core.protocol.MarshallingType;
import software.amazon.awssdk.core.traits.DataTypeConversionFailureHandlingTrait;
import software.amazon.awssdk.core.traits.DefaultValueTrait;
import software.amazon.awssdk.core.traits.JsonValueTrait;
import software.amazon.awssdk.core.traits.ListTrait;
Expand Down Expand Up @@ -186,6 +187,10 @@ private CodeBlock traits(MemberModel m) {
if (m.isXmlAttribute()) {
traits.add(createXmlAttributeTrait());
}

if (m.ignoreDataTypeConversionFailures()) {
traits.add(createDataTypeConversionFailureHandlingTrait());
}
if (customizationConfig.isRequiredTraitValidationEnabled() && m.isRequired()) {
traits.add(createRequiredTrait());
}
Expand All @@ -206,6 +211,12 @@ private boolean attachPayloadTraitToMember(MemberModel m) {
.equals(m.getC2jName());
}

private CodeBlock createDataTypeConversionFailureHandlingTrait() {
return CodeBlock.builder()
.add("new $T()", ClassName.get(DataTypeConversionFailureHandlingTrait.class))
.build();
}

private CodeBlock createTimestampFormatTrait(MemberModel m) {
TimestampFormatTrait.Format format = TimestampFormatTrait.Format.fromString(m.getTimestampFormat());
ClassName traitClass = ClassName.get(TimestampFormatTrait.class);
Expand Down
Expand Up @@ -26,6 +26,11 @@
"deprecated": true,
"deprecatedMessage": "This field is modified as deprecated."
}
},
{
"MemberIgnoreDataTypeFailureHandling": {
"ignoreDataTypeConversionFailures": "true"
}
}
]
},
Expand Down
Expand Up @@ -14,6 +14,7 @@
import software.amazon.awssdk.core.SdkPojo;
import software.amazon.awssdk.core.protocol.MarshallLocation;
import software.amazon.awssdk.core.protocol.MarshallingType;
import software.amazon.awssdk.core.traits.DataTypeConversionFailureHandlingTrait;
import software.amazon.awssdk.core.traits.LocationTrait;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
Expand Down Expand Up @@ -45,20 +46,33 @@ public final class OperationWithDeprecatedMemberRequest extends JsonProtocolTest
.traits(LocationTrait.builder().location(MarshallLocation.PAYLOAD).locationName("UndeprecatedMember").build())
.build();

private static final SdkField<String> MEMBER_IGNORE_DATA_TYPE_FAILURE_HANDLING_FIELD = SdkField
.<String> builder(MarshallingType.STRING)
.memberName("MemberIgnoreDataTypeFailureHandling")
.getter(getter(OperationWithDeprecatedMemberRequest::memberIgnoreDataTypeFailureHandling))
.setter(setter(Builder::memberIgnoreDataTypeFailureHandling))
.traits(LocationTrait.builder().location(MarshallLocation.PAYLOAD)
.locationName("MemberIgnoreDataTypeFailureHandling").build(), new DataTypeConversionFailureHandlingTrait())
.build();

private static final List<SdkField<?>> SDK_FIELDS = Collections.unmodifiableList(Arrays.asList(
MEMBER_MODELED_AS_DEPRECATED_FIELD, MEMBER_MODIFIED_AS_DEPRECATED_FIELD, UNDEPRECATED_MEMBER_FIELD));
MEMBER_MODELED_AS_DEPRECATED_FIELD, MEMBER_MODIFIED_AS_DEPRECATED_FIELD, UNDEPRECATED_MEMBER_FIELD,
MEMBER_IGNORE_DATA_TYPE_FAILURE_HANDLING_FIELD));

private final String memberModeledAsDeprecated;

private final String memberModifiedAsDeprecated;

private final String undeprecatedMember;

private final String memberIgnoreDataTypeFailureHandling;

private OperationWithDeprecatedMemberRequest(BuilderImpl builder) {
super(builder);
this.memberModeledAsDeprecated = builder.memberModeledAsDeprecated;
this.memberModifiedAsDeprecated = builder.memberModifiedAsDeprecated;
this.undeprecatedMember = builder.undeprecatedMember;
this.memberIgnoreDataTypeFailureHandling = builder.memberIgnoreDataTypeFailureHandling;
}

/**
Expand Down Expand Up @@ -92,6 +106,15 @@ public final String undeprecatedMember() {
return undeprecatedMember;
}

/**
* Returns the value of the MemberIgnoreDataTypeFailureHandling property for this object.
*
* @return The value of the MemberIgnoreDataTypeFailureHandling property for this object.
*/
public final String memberIgnoreDataTypeFailureHandling() {
return memberIgnoreDataTypeFailureHandling;
}

@Override
public Builder toBuilder() {
return new BuilderImpl(this);
Expand All @@ -112,6 +135,7 @@ public final int hashCode() {
hashCode = 31 * hashCode + Objects.hashCode(memberModeledAsDeprecated());
hashCode = 31 * hashCode + Objects.hashCode(memberModifiedAsDeprecated());
hashCode = 31 * hashCode + Objects.hashCode(undeprecatedMember());
hashCode = 31 * hashCode + Objects.hashCode(memberIgnoreDataTypeFailureHandling());
return hashCode;
}

Expand All @@ -134,7 +158,8 @@ public final boolean equalsBySdkFields(Object obj) {
OperationWithDeprecatedMemberRequest other = (OperationWithDeprecatedMemberRequest) obj;
return Objects.equals(memberModeledAsDeprecated(), other.memberModeledAsDeprecated())
&& Objects.equals(memberModifiedAsDeprecated(), other.memberModifiedAsDeprecated())
&& Objects.equals(undeprecatedMember(), other.undeprecatedMember());
&& Objects.equals(undeprecatedMember(), other.undeprecatedMember())
&& Objects.equals(memberIgnoreDataTypeFailureHandling(), other.memberIgnoreDataTypeFailureHandling());
}

/**
Expand All @@ -146,7 +171,7 @@ public final String toString() {
return ToString.builder("OperationWithDeprecatedMemberRequest")
.add("MemberModeledAsDeprecated", memberModeledAsDeprecated())
.add("MemberModifiedAsDeprecated", memberModifiedAsDeprecated()).add("UndeprecatedMember", undeprecatedMember())
.build();
.add("MemberIgnoreDataTypeFailureHandling", memberIgnoreDataTypeFailureHandling()).build();
}

public final <T> Optional<T> getValueForField(String fieldName, Class<T> clazz) {
Expand All @@ -157,6 +182,8 @@ public final <T> Optional<T> getValueForField(String fieldName, Class<T> clazz)
return Optional.ofNullable(clazz.cast(memberModifiedAsDeprecated()));
case "UndeprecatedMember":
return Optional.ofNullable(clazz.cast(undeprecatedMember()));
case "MemberIgnoreDataTypeFailureHandling":
return Optional.ofNullable(clazz.cast(memberIgnoreDataTypeFailureHandling()));
default:
return Optional.empty();
}
Expand Down Expand Up @@ -208,6 +235,15 @@ public interface Builder extends JsonProtocolTestsRequest.Builder, SdkPojo,
*/
Builder undeprecatedMember(String undeprecatedMember);

/**
* Sets the value of the MemberIgnoreDataTypeFailureHandling property for this object.
*
* @param memberIgnoreDataTypeFailureHandling
* The new value for the MemberIgnoreDataTypeFailureHandling property for this object.
* @return Returns a reference to this object so that method calls can be chained together.
*/
Builder memberIgnoreDataTypeFailureHandling(String memberIgnoreDataTypeFailureHandling);

@Override
Builder overrideConfiguration(AwsRequestOverrideConfiguration overrideConfiguration);

Expand All @@ -222,6 +258,8 @@ static final class BuilderImpl extends JsonProtocolTestsRequest.BuilderImpl impl

private String undeprecatedMember;

private String memberIgnoreDataTypeFailureHandling;

private BuilderImpl() {
}

Expand All @@ -230,6 +268,7 @@ private BuilderImpl(OperationWithDeprecatedMemberRequest model) {
memberModeledAsDeprecated(model.memberModeledAsDeprecated);
memberModifiedAsDeprecated(model.memberModifiedAsDeprecated);
undeprecatedMember(model.undeprecatedMember);
memberIgnoreDataTypeFailureHandling(model.memberIgnoreDataTypeFailureHandling);
}

@Deprecated
Expand Down Expand Up @@ -280,6 +319,20 @@ public final Builder undeprecatedMember(String undeprecatedMember) {
return this;
}

public final String getMemberIgnoreDataTypeFailureHandling() {
return memberIgnoreDataTypeFailureHandling;
}

public final void setMemberIgnoreDataTypeFailureHandling(String memberIgnoreDataTypeFailureHandling) {
this.memberIgnoreDataTypeFailureHandling = memberIgnoreDataTypeFailureHandling;
}

@Override
public final Builder memberIgnoreDataTypeFailureHandling(String memberIgnoreDataTypeFailureHandling) {
this.memberIgnoreDataTypeFailureHandling = memberIgnoreDataTypeFailureHandling;
return this;
}

@Override
public Builder overrideConfiguration(AwsRequestOverrideConfiguration overrideConfiguration) {
super.overrideConfiguration(overrideConfiguration);
Expand Down
Expand Up @@ -247,7 +247,8 @@
"deprecatedMessage": "This field is modeled as deprecated."
},
"MemberModifiedAsDeprecated":{"shape": "String"},
"UndeprecatedMember": {"shape": "String"}
"UndeprecatedMember": {"shape": "String"},
"MemberIgnoreDataTypeFailureHandling":{"shape": "String"}
}
},
"OperationWithDeprecatedMemberResponse":{
Expand Down
Expand Up @@ -26,6 +26,7 @@
import software.amazon.awssdk.core.SdkField;
import software.amazon.awssdk.protocols.core.StringToValueConverter;
import software.amazon.awssdk.protocols.query.unmarshall.XmlElement;
import software.amazon.awssdk.utils.Logger;

@SdkInternalApi
public final class HeaderUnmarshaller {
Expand Down Expand Up @@ -60,6 +61,7 @@ private HeaderUnmarshaller() {
}

private static class SimpleHeaderUnmarshaller<T> implements XmlUnmarshaller<T> {
private static final Logger log = Logger.loggerFor(SimpleHeaderUnmarshaller.class);

private final StringToValueConverter.StringToValue<T> stringToValue;

Expand All @@ -69,9 +71,17 @@ private SimpleHeaderUnmarshaller(StringToValueConverter.StringToValue<T> stringT

@Override
public T unmarshall(XmlUnmarshallerContext context, List<XmlElement> content, SdkField<T> field) {
return context.response().firstMatchingHeader(field.locationName())
.map(s -> stringToValue.convert(s, field))
.orElse(null);
try {
return context.response().firstMatchingHeader(field.locationName())
.map(s -> stringToValue.convert(s, field))
.orElse(null);
} catch (RuntimeException e) {
log.warn(() -> "Exception found while parsing response header " , e);
if (field.ignoreDataTypeConversionFailures()) {
return null;
}
throw e;
}
}
}
}
Expand Up @@ -25,6 +25,7 @@
import software.amazon.awssdk.annotations.SdkProtectedApi;
import software.amazon.awssdk.core.protocol.MarshallLocation;
import software.amazon.awssdk.core.protocol.MarshallingType;
import software.amazon.awssdk.core.traits.DataTypeConversionFailureHandlingTrait;
import software.amazon.awssdk.core.traits.DefaultValueTrait;
import software.amazon.awssdk.core.traits.LocationTrait;
import software.amazon.awssdk.core.traits.Trait;
Expand Down Expand Up @@ -86,6 +87,16 @@ public String locationName() {
return locationName;
}

/**
* @return whether data-type conversion errors are to be ignored
*/
public boolean ignoreDataTypeConversionFailures() {
DataTypeConversionFailureHandlingTrait dataTypeConversionFailureHandlingTrait =
getTrait(DataTypeConversionFailureHandlingTrait.class);

return dataTypeConversionFailureHandlingTrait != null;
}

/**
* @return The location name to use when unmarshalling. This is only needed for AWS/Query or EC2 services. All
* other services should use {@link #locationName} for both marshalling and unmarshalling.
Expand Down
@@ -0,0 +1,22 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.core.traits;

import software.amazon.awssdk.annotations.SdkProtectedApi;

@SdkProtectedApi
public class DataTypeConversionFailureHandlingTrait implements Trait {
}

0 comments on commit 31ec0e7

Please sign in to comment.