Skip to content

Commit f8c9121

Browse files
authored
fix: s3 custom treatment getbucketlocation response (#989)
1 parent 28d9a48 commit f8c9121

File tree

7 files changed

+148
-82
lines changed

7 files changed

+148
-82
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "4197c56c-e17a-4fb3-a680-4ca384267d2b",
3+
"type": "bugfix",
4+
"description": "Correctly parse and handle `GetBucketLocation` responses",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#194"
7+
]
8+
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/customization/s3/GetBucketLocationDeserializerIntegration.kt

Lines changed: 0 additions & 40 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.sdk.kotlin.codegen.customization.s3
6+
7+
import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait
8+
import software.amazon.smithy.kotlin.codegen.KotlinSettings
9+
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
10+
import software.amazon.smithy.kotlin.codegen.model.expectShape
11+
import software.amazon.smithy.kotlin.codegen.model.hasTrait
12+
import software.amazon.smithy.kotlin.codegen.model.traits.UnwrappedXmlOutput
13+
import software.amazon.smithy.model.Model
14+
import software.amazon.smithy.model.shapes.ServiceShape
15+
import software.amazon.smithy.model.shapes.StructureShape
16+
import software.amazon.smithy.model.transform.ModelTransformer
17+
18+
/**
19+
* Applies the [UnwrappedXmlOutput] custom-made [annotation trait](https://smithy.io/2.0/spec/model.html?highlight=annotation#annotation-traits) to structures
20+
* whose operation is annotated with `S3UnwrappedXmlOutput` trait to mark when special unwrapped xml output deserialization is required.
21+
*/
22+
class UnwrappedXmlOutputIntegration : KotlinIntegration {
23+
override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
24+
model.expectShape<ServiceShape>(settings.service).isS3
25+
26+
override fun preprocessModel(model: Model, settings: KotlinSettings): Model {
27+
val unwrappedStructures = model
28+
.operationShapes
29+
.filter { it.hasTrait<S3UnwrappedXmlOutputTrait>() }
30+
.map { it.outputShape }
31+
.toSet()
32+
33+
return ModelTransformer
34+
.create()
35+
.mapShapes(model) { shape ->
36+
when {
37+
shape.id in unwrappedStructures ->
38+
(shape as StructureShape).toBuilder().addTrait(UnwrappedXmlOutput()).build()
39+
else -> shape
40+
}
41+
}
42+
}
43+
}

codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ aws.sdk.kotlin.codegen.GradleGenerator
66
aws.sdk.kotlin.codegen.AwsServiceConfigIntegration
77
aws.sdk.kotlin.codegen.customization.s3.S3SigningConfig
88
aws.sdk.kotlin.codegen.customization.s3.S3ErrorMetadataIntegration
9-
aws.sdk.kotlin.codegen.customization.s3.GetBucketLocationDeserializerIntegration
109
aws.sdk.kotlin.codegen.customization.PresignableModelIntegration
1110
aws.sdk.kotlin.codegen.PresignerGenerator
1211
aws.sdk.kotlin.codegen.customization.apigateway.ApiGatewayAddAcceptHeader
@@ -31,3 +30,4 @@ aws.sdk.kotlin.codegen.customization.s3control.HostPrefixFilter
3130
aws.sdk.kotlin.codegen.customization.s3control.ClientConfigIntegration
3231
aws.sdk.kotlin.codegen.protocols.endpoints.BindAwsEndpointBuiltins
3332
aws.sdk.kotlin.codegen.customization.s3.HostPrefixRequestRouteFilter
33+
aws.sdk.kotlin.codegen.customization.s3.UnwrappedXmlOutputIntegration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.sdk.kotlin.codegen.customization.s3
6+
7+
import aws.sdk.kotlin.codegen.testutil.model
8+
import org.junit.jupiter.api.Assertions.assertFalse
9+
import org.junit.jupiter.api.Test
10+
import software.amazon.smithy.kotlin.codegen.test.defaultSettings
11+
import kotlin.test.assertTrue
12+
13+
/**
14+
* Verify [UnwrappedXmlOutputIntegration] is enabled for proper service (S3)
15+
*/
16+
class UnwrappedXmlOutputIntegrationTest {
17+
@Test
18+
fun testNonS3Model() {
19+
val model = model("NotS3")
20+
val actual = UnwrappedXmlOutputIntegration().enabledForService(model, model.defaultSettings())
21+
assertFalse(actual)
22+
}
23+
24+
@Test
25+
fun testS3Model() {
26+
val model = model("S3")
27+
val actual = UnwrappedXmlOutputIntegration().enabledForService(model, model.defaultSettings())
28+
assertTrue(actual)
29+
}
30+
}

services/s3/common/src/aws/sdk/kotlin/services/s3/internal/GetBucketLocationOperationDeserializer.kt

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package aws.sdk.kotlin.services.s3.internal
7+
8+
import aws.sdk.kotlin.services.s3.model.BucketLocationConstraint
9+
import aws.sdk.kotlin.services.s3.model.S3Exception
10+
import aws.sdk.kotlin.services.s3.transform.GetBucketLocationOperationDeserializer
11+
import aws.smithy.kotlin.runtime.http.Headers
12+
import aws.smithy.kotlin.runtime.http.HttpBody
13+
import aws.smithy.kotlin.runtime.http.HttpStatusCode
14+
import aws.smithy.kotlin.runtime.http.response.HttpResponse
15+
import aws.smithy.kotlin.runtime.operation.ExecutionContext
16+
import kotlinx.coroutines.runBlocking
17+
import org.junit.jupiter.api.Assertions.assertEquals
18+
import org.junit.jupiter.api.Test
19+
import org.junit.jupiter.api.assertThrows
20+
21+
class GetBucketLocationOperationDeserializerTest {
22+
@Test
23+
fun deserializeUnwrappedResponse() {
24+
val responseXML = """
25+
<?xml version="1.0" encoding="UTF-8"?>
26+
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">us-west-2</LocationConstraint>
27+
""".trimIndent()
28+
29+
val response: HttpResponse = HttpResponse(
30+
HttpStatusCode(200, "Success"),
31+
Headers.invoke { },
32+
HttpBody.fromBytes(responseXML.encodeToByteArray()),
33+
)
34+
35+
val actual = runBlocking {
36+
GetBucketLocationOperationDeserializer().deserialize(ExecutionContext(), response)
37+
}
38+
39+
assertEquals(BucketLocationConstraint.UsWest2, actual.locationConstraint)
40+
}
41+
42+
@Test
43+
fun deserializeErrorMessage() {
44+
val responseXML = """
45+
<?xml version="1.0" encoding="UTF-8"?>
46+
<Error>
47+
<Message>Some message</Message>
48+
<RequestId>Some request ID</RequestId>
49+
</Error>
50+
""".trimIndent()
51+
52+
val response: HttpResponse = HttpResponse(
53+
HttpStatusCode(400, "Bad Request"),
54+
Headers.invoke { },
55+
HttpBody.fromBytes(responseXML.encodeToByteArray()),
56+
)
57+
58+
val exception = assertThrows<S3Exception> {
59+
runBlocking {
60+
GetBucketLocationOperationDeserializer().deserialize(ExecutionContext(), response)
61+
}
62+
}
63+
64+
assertEquals("Some message", exception.message)
65+
}
66+
}

0 commit comments

Comments
 (0)