Skip to content

Commit 40bf65d

Browse files
authored
misc: add Accept header to all RpcV2Cbor requests (#1170)
1 parent 32f1ed8 commit 40bf65d

File tree

5 files changed

+137
-9
lines changed

5 files changed

+137
-9
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "2daf7e93-a9e4-4c6b-9c0c-b083a1864c09",
3+
"type": "misc",
4+
"description": "Add `Accept` header to all RpcV2Cbor requests"
5+
}

codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RpcV2Cbor.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait
2626
import software.amazon.smithy.model.traits.UnitTypeTrait
2727
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
2828

29+
private const val ACCEPT_HEADER = "application/cbor"
30+
private const val ACCEPT_HEADER_EVENT_STREAM = "application/vnd.amazon.eventstream"
31+
2932
class RpcV2Cbor : AwsHttpBindingProtocolGenerator() {
3033
override val protocol: ShapeId = Rpcv2CborTrait.ID
3134

@@ -59,13 +62,16 @@ class RpcV2Cbor : AwsHttpBindingProtocolGenerator() {
5962
}
6063
}
6164

62-
// Requests with event stream responses MUST include an `Accept` header set to the value `application/vnd.amazon.eventstream`
63-
val eventStreamsAcceptHeaderMiddleware = object : ProtocolMiddleware {
64-
private val mutateHeadersMiddleware = MutateHeadersMiddleware(extraHeaders = mapOf("Accept" to "application/vnd.amazon.eventstream"))
65-
66-
override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean = op.isOutputEventStream(ctx.model)
67-
override val name: String = "RpcV2CborEventStreamsAcceptHeaderMiddleware"
68-
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) = mutateHeadersMiddleware.render(ctx, op, writer)
65+
// Add `Accept` header with value `application/cbor` for standard responses
66+
// and `application/vnd.amazon.eventstream` for event stream responses
67+
val acceptHeaderMiddleware = object : ProtocolMiddleware {
68+
override val name: String = "RpcV2CborAcceptHeaderMiddleware"
69+
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) {
70+
val acceptHeaderValue = if (op.isOutputEventStream(ctx.model)) ACCEPT_HEADER_EVENT_STREAM else ACCEPT_HEADER
71+
MutateHeadersMiddleware(
72+
extraHeaders = mapOf("Accept" to acceptHeaderValue),
73+
).render(ctx, op, writer)
74+
}
6975
}
7076

7177
// Emit a metric to track usage of RpcV2Cbor
@@ -79,7 +85,7 @@ class RpcV2Cbor : AwsHttpBindingProtocolGenerator() {
7985
return super.getDefaultHttpMiddleware(ctx) + listOf(
8086
smithyProtocolHeaderMiddleware,
8187
validateSmithyProtocolHeaderMiddleware,
82-
eventStreamsAcceptHeaderMiddleware,
88+
acceptHeaderMiddleware,
8389
businessMetricsMiddleware,
8490
)
8591
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.kotlin.codegen.aws.protocols
6+
7+
import software.amazon.smithy.kotlin.codegen.test.*
8+
import kotlin.test.Test
9+
10+
class RpcV2CborTest {
11+
val model = """
12+
${"$"}version: "2"
13+
14+
namespace com.test
15+
16+
use smithy.protocols#rpcv2Cbor
17+
use aws.api#service
18+
19+
@rpcv2Cbor
20+
@service(sdkId: "CborExample")
21+
service CborExample {
22+
version: "1.0.0",
23+
operations: [GetFoo, GetFooStreaming]
24+
}
25+
26+
@http(method: "POST", uri: "/foo")
27+
operation GetFoo {}
28+
29+
@http(method: "POST", uri: "/foo-streaming")
30+
operation GetFooStreaming {
31+
input := {}
32+
output := {
33+
events: FooEvents
34+
}
35+
}
36+
37+
// Model taken from https://smithy.io/2.0/spec/streaming.html#event-streams
38+
@streaming
39+
union FooEvents {
40+
up: Movement
41+
down: Movement
42+
left: Movement
43+
right: Movement
44+
throttlingError: ThrottlingError
45+
}
46+
47+
structure Movement {
48+
velocity: Float
49+
}
50+
51+
@error("client")
52+
@retryable(throttling: true)
53+
structure ThrottlingError {}
54+
""".toSmithyModel()
55+
56+
@Test
57+
fun testStandardAcceptHeader() {
58+
val ctx = model.newTestContext("CborExample")
59+
60+
val generator = RpcV2Cbor()
61+
generator.generateProtocolClient(ctx.generationCtx)
62+
63+
ctx.generationCtx.delegator.finalize()
64+
ctx.generationCtx.delegator.flushWriters()
65+
66+
val actual = ctx.manifest.expectFileString("/src/main/kotlin/com/test/DefaultTestClient.kt")
67+
val getFooMethod = actual.lines(" override suspend fun getFoo(input: GetFooRequest): GetFooResponse {", " }")
68+
69+
val expectedHeaderMutation = """
70+
op.install(
71+
MutateHeaders().apply {
72+
append("Accept", "application/cbor")
73+
}
74+
)
75+
""".replaceIndent(" ")
76+
getFooMethod.shouldContainOnlyOnceWithDiff(expectedHeaderMutation)
77+
}
78+
79+
@Test
80+
fun testEventStreamAcceptHeader() {
81+
val ctx = model.newTestContext("CborExample")
82+
83+
val generator = RpcV2Cbor()
84+
generator.generateProtocolClient(ctx.generationCtx)
85+
86+
ctx.generationCtx.delegator.finalize()
87+
ctx.generationCtx.delegator.flushWriters()
88+
89+
val actual = ctx.manifest.expectFileString("/src/main/kotlin/com/test/DefaultTestClient.kt")
90+
val getFooMethod = actual.lines(" override suspend fun <T> getFooStreaming(input: GetFooStreamingRequest, block: suspend (GetFooStreamingResponse) -> T): T {", " }")
91+
92+
val expectedHeaderMutation = """
93+
op.install(
94+
MutateHeaders().apply {
95+
append("Accept", "application/vnd.amazon.eventstream")
96+
}
97+
)
98+
""".replaceIndent(" ")
99+
getFooMethod.shouldContainOnlyOnceWithDiff(expectedHeaderMutation)
100+
}
101+
}

codegen/smithy-kotlin-codegen-testutils/src/main/kotlin/software/amazon/smithy/kotlin/codegen/test/CodegenTestUtils.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import software.amazon.smithy.model.traits.TimestampFormatTrait
2828
import software.amazon.smithy.model.traits.Trait
2929
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
3030
import software.amazon.smithy.utils.StringUtils
31+
import kotlin.test.assertNotEquals
3132

3233
// This file houses test classes and functions relating to the code generator (protocols, serializers, etc)
3334
// Items contained here should be relatively high-level, utilizing all members of codegen classes, Smithy, and
@@ -274,3 +275,19 @@ fun KotlinCodegenPlugin.Companion.createSymbolProvider(
274275
* create a new [KotlinWriter] using the test context package name
275276
*/
276277
fun TestContext.newWriter(): KotlinWriter = KotlinWriter(generationCtx.settings.pkg.name)
278+
279+
/**
280+
* Get all the lines between [fromLine] and [toLine]
281+
*/
282+
fun String.lines(fromLine: String, toLine: String): String {
283+
val allLines = lines()
284+
285+
val fromIdx = allLines.indexOf(fromLine)
286+
assertNotEquals(-1, fromIdx, """Could not find from line "$fromLine" in all lines""")
287+
288+
val toIdxOffset = allLines.drop(fromIdx + 1).indexOf(toLine)
289+
assertNotEquals(-1, toIdxOffset, """Could not find to line "$toLine" in all lines""")
290+
291+
val toIdx = toIdxOffset + fromIdx + 1
292+
return allLines.subList(fromIdx, toIdx + 1).joinToString("\n")
293+
}

runtime/serde/serde-xml/common/test/aws/smithy/kotlin/runtime/serde/xml/XmlStreamReaderTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ class XmlStreamReaderTest {
108108
""".trimIndent().encodeToByteArray()
109109

110110
val actual = xmlStreamReader(payload).allTokens()
111-
println(actual)
112111

113112
assertEquals(6, actual.size)
114113
assertIs<XmlToken.BeginElement>(actual.first())

0 commit comments

Comments
 (0)