1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.protocol.tests.exception; 17 18 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; 19 import static com.github.tomakehurst.wiremock.client.WireMock.post; 20 import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; 21 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; 22 import static org.assertj.core.api.Assertions.assertThat; 23 import static org.junit.Assert.assertEquals; 24 import static software.amazon.awssdk.protocol.tests.util.exception.ExceptionTestUtils.stub404Response; 25 26 import com.github.tomakehurst.wiremock.junit.WireMockRule; 27 import java.net.URI; 28 import java.time.Instant; 29 import java.util.AbstractMap.SimpleEntry; 30 import org.junit.Before; 31 import org.junit.Rule; 32 import org.junit.Test; 33 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 34 import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 35 import software.amazon.awssdk.awscore.exception.AwsErrorDetails; 36 import software.amazon.awssdk.core.exception.SdkClientException; 37 import software.amazon.awssdk.regions.Region; 38 import software.amazon.awssdk.services.protocolrestxml.ProtocolRestXmlClient; 39 import software.amazon.awssdk.services.protocolrestxml.model.AllTypesRequest; 40 import software.amazon.awssdk.services.protocolrestxml.model.EmptyModeledException; 41 import software.amazon.awssdk.services.protocolrestxml.model.ImplicitPayloadException; 42 import software.amazon.awssdk.services.protocolrestxml.model.MultiLocationOperationRequest; 43 import software.amazon.awssdk.services.protocolrestxml.model.ProtocolRestXmlException; 44 45 public class RestXmlExceptionTests { 46 47 private static final String ALL_TYPES_PATH = "/2016-03-11/allTypes"; 48 49 @Rule 50 public WireMockRule wireMock = new WireMockRule(0); 51 52 private ProtocolRestXmlClient client; 53 54 @Before setupClient()55 public void setupClient() { 56 client = ProtocolRestXmlClient.builder() 57 .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) 58 .region(Region.US_EAST_1) 59 .endpointOverride(URI.create("http://localhost:" + wireMock.port())) 60 .build(); 61 } 62 63 @Test unmodeledException_UnmarshalledIntoBaseServiceException()64 public void unmodeledException_UnmarshalledIntoBaseServiceException() { 65 stub404Response(ALL_TYPES_PATH, 66 "<ErrorResponse><Error><Code>UnmodeledException</Code></Error></ErrorResponse>"); 67 assertThrowsServiceBaseException(this::callAllTypes); 68 } 69 70 @Test modeledException_UnmarshalledIntoModeledException()71 public void modeledException_UnmarshalledIntoModeledException() { 72 stub404Response(ALL_TYPES_PATH, 73 "<ErrorResponse><Error><Code>EmptyModeledException</Code></Error></ErrorResponse>"); 74 try { 75 callAllTypes(); 76 } catch (EmptyModeledException e) { 77 assertThat(e).isInstanceOf(ProtocolRestXmlException.class); 78 } 79 } 80 81 @Test modeledExceptionWithImplicitPayloadMembers_UnmarshalledIntoModeledException()82 public void modeledExceptionWithImplicitPayloadMembers_UnmarshalledIntoModeledException() { 83 String xml = "<ErrorResponse>" 84 + " <Error>" 85 + " <Code>ImplicitPayloadException</Code>" 86 + " <Message>this is the service message</Message>" 87 + " <StringMember>foo</StringMember>" 88 + " <IntegerMember>42</IntegerMember>" 89 + " <LongMember>9001</LongMember>" 90 + " <DoubleMember>1234.56</DoubleMember>" 91 + " <FloatMember>789.10</FloatMember>" 92 + " <TimestampMember>2015-01-25T08:00:12Z</TimestampMember>" 93 + " <BooleanMember>true</BooleanMember>" 94 + " <BlobMember>dGhlcmUh</BlobMember>" 95 + " <ListMember>" 96 + " <member>valOne</member>" 97 + " <member>valTwo</member>" 98 + " </ListMember>" 99 + " <MapMember>" 100 + " <entry>" 101 + " <key>keyOne</key>" 102 + " <value>valOne</value>" 103 + " </entry>" 104 + " <entry>" 105 + " <key>keyTwo</key>" 106 + " <value>valTwo</value>" 107 + " </entry>" 108 + " </MapMember>" 109 + " <SimpleStructMember>" 110 + " <StringMember>foobar</StringMember>" 111 + " </SimpleStructMember>" 112 + " </Error>" 113 + "</ErrorResponse>"; 114 stubFor(post(urlEqualTo(ALL_TYPES_PATH)).willReturn( 115 aResponse().withStatus(404) 116 .withBody(xml))); 117 try { 118 client.allTypes(); 119 } catch (ImplicitPayloadException e) { 120 assertThat(e.stringMember()).isEqualTo("foo"); 121 assertThat(e.integerMember()).isEqualTo(42); 122 assertThat(e.longMember()).isEqualTo(9001); 123 assertThat(e.doubleMember()).isEqualTo(1234.56); 124 assertThat(e.floatMember()).isEqualTo(789.10f); 125 assertThat(e.timestampMember()).isEqualTo(Instant.ofEpochMilli(1422172812000L)); 126 assertThat(e.booleanMember()).isEqualTo(true); 127 assertThat(e.blobMember().asUtf8String()).isEqualTo("there!"); 128 assertThat(e.listMember()).contains("valOne", "valTwo"); 129 assertThat(e.mapMember()) 130 .containsOnly(new SimpleEntry<>("keyOne", "valOne"), 131 new SimpleEntry<>("keyTwo", "valTwo")); 132 assertThat(e.simpleStructMember().stringMember()).isEqualTo("foobar"); 133 } 134 } 135 136 @Test modeledException_HasExceptionMetadataSet()137 public void modeledException_HasExceptionMetadataSet() { 138 String xml = "<ErrorResponse>" 139 + " <Error>" 140 + " <Code>EmptyModeledException</Code>" 141 + " <Message>This is the service message</Message>" 142 + " </Error>" 143 + " <RequestId>1234</RequestId>" 144 + "</ErrorResponse>"; 145 stubFor(post(urlEqualTo(ALL_TYPES_PATH)).willReturn( 146 aResponse() 147 .withStatus(404) 148 .withBody(xml))); 149 try { 150 client.allTypes(); 151 } catch (EmptyModeledException e) { 152 AwsErrorDetails awsErrorDetails = e.awsErrorDetails(); 153 assertThat(awsErrorDetails.errorCode()).isEqualTo("EmptyModeledException"); 154 assertThat(awsErrorDetails.errorMessage()).isEqualTo("This is the service message"); 155 assertThat(awsErrorDetails.serviceName()).isEqualTo("ProtocolRestXml"); 156 assertThat(awsErrorDetails.sdkHttpResponse()).isNotNull(); 157 assertThat(e.requestId()).isEqualTo("1234"); 158 assertThat(e.extendedRequestId()).isNull(); 159 assertThat(e.statusCode()).isEqualTo(404); 160 } 161 } 162 163 @Test modeledException_HasExceptionMetadataIncludingExtendedRequestIdSet()164 public void modeledException_HasExceptionMetadataIncludingExtendedRequestIdSet() { 165 String xml = "<ErrorResponse>" 166 + " <Error>" 167 + " <Code>EmptyModeledException</Code>" 168 + " <Message>This is the service message</Message>" 169 + " </Error>" 170 + " <RequestId>1234</RequestId>" 171 + "</ErrorResponse>"; 172 stubFor(post(urlEqualTo(ALL_TYPES_PATH)).willReturn( 173 aResponse() 174 .withStatus(404) 175 .withHeader("x-amz-id-2", "5678") 176 .withBody(xml))); 177 try { 178 client.allTypes(); 179 } catch (EmptyModeledException e) { 180 AwsErrorDetails awsErrorDetails = e.awsErrorDetails(); 181 assertThat(awsErrorDetails.errorCode()).isEqualTo("EmptyModeledException"); 182 assertThat(awsErrorDetails.errorMessage()).isEqualTo("This is the service message"); 183 assertThat(awsErrorDetails.serviceName()).isEqualTo("ProtocolRestXml"); 184 assertThat(awsErrorDetails.sdkHttpResponse()).isNotNull(); 185 assertThat(e.requestId()).isEqualTo("1234"); 186 assertThat(e.extendedRequestId()).isEqualTo("5678"); 187 assertThat(e.statusCode()).isEqualTo(404); 188 } 189 } 190 191 @Test emptyErrorResponse_UnmarshalledIntoBaseServiceException()192 public void emptyErrorResponse_UnmarshalledIntoBaseServiceException() { 193 stub404Response(ALL_TYPES_PATH, ""); 194 assertThrowsServiceBaseException(this::callAllTypes); 195 } 196 197 @Test malformedErrorResponse_UnmarshalledIntoBaseServiceException()198 public void malformedErrorResponse_UnmarshalledIntoBaseServiceException() { 199 stub404Response(ALL_TYPES_PATH, "THIS ISN'T XML"); 200 assertThrowsServiceBaseException(this::callAllTypes); 201 } 202 203 @Test illegalArgumentException_nullPathParam()204 public void illegalArgumentException_nullPathParam() { 205 assertThrowsNestedExceptions(() -> client.multiLocationOperation(MultiLocationOperationRequest.builder().build()), 206 SdkClientException.class, 207 IllegalArgumentException.class); 208 } 209 210 @Test illegalArgumentException_emptyPathParam()211 public void illegalArgumentException_emptyPathParam() { 212 assertThrowsNestedExceptions(() -> client.multiLocationOperation(MultiLocationOperationRequest.builder() 213 .pathParam("") 214 .build()), 215 SdkClientException.class, 216 IllegalArgumentException.class); 217 } 218 219 @Test alternateRequestIdInHeader_IsSetOnException()220 public void alternateRequestIdInHeader_IsSetOnException() { 221 stubFor(post(urlEqualTo(ALL_TYPES_PATH)).willReturn( 222 aResponse() 223 .withStatus(404) 224 .withHeader("x-amz-request-id", "1234"))); 225 try { 226 client.allTypes(); 227 } catch (ProtocolRestXmlException e) { 228 assertThat(e.requestId()).isEqualTo("1234"); 229 } 230 } 231 callAllTypes()232 private void callAllTypes() { 233 client.allTypes(AllTypesRequest.builder().build()); 234 } 235 assertThrowsServiceBaseException(Runnable runnable)236 private void assertThrowsServiceBaseException(Runnable runnable) { 237 assertThrowsException(runnable, ProtocolRestXmlException.class); 238 } 239 assertThrowsIllegalArgumentException(Runnable runnable)240 private void assertThrowsIllegalArgumentException(Runnable runnable) { 241 assertThrowsException(runnable, IllegalArgumentException.class); 242 } 243 assertThrowsNullPointerException(Runnable runnable)244 private void assertThrowsNullPointerException(Runnable runnable) { 245 assertThrowsException(runnable, NullPointerException.class); 246 } 247 assertThrowsException(Runnable runnable, Class<? extends Exception> expectedException)248 private void assertThrowsException(Runnable runnable, Class<? extends Exception> expectedException) { 249 try { 250 runnable.run(); 251 } catch (Exception e) { 252 assertEquals(expectedException, e.getClass()); 253 } 254 } 255 assertThrowsNestedExceptions(Runnable runnable, Class<? extends Exception> parentException, Class<? extends Exception> nestedException)256 private void assertThrowsNestedExceptions(Runnable runnable, Class<? extends Exception> parentException, 257 Class<? extends Exception> nestedException) { 258 try { 259 runnable.run(); 260 } catch (Exception e) { 261 assertEquals(parentException, e.getClass()); 262 assertEquals(nestedException, e.getCause().getClass()); 263 } 264 } 265 } 266