Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ val lintPaths = listOf(
"dokka-aws/**/*.kt",
"gradle/sdk-plugins/src/**/*.kt",
"services/**/*.kt",
"!services/*/generated-src/**/*.kt"
"!services/*/generated-src/**/*.kt",
"tests/**/*.kt"
)

tasks.register<JavaExec>("ktlint") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,17 @@ class RestXmlParserGenerator(
writer: KotlinWriter
): XmlSerdeDescriptorGenerator = RestXmlSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members)

override fun payloadDeserializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
override fun payloadDeserializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape>?
): Symbol {
return if (shape.hasTrait<XmlNameTrait>() && shape is MemberShape) {
// can't delegate, have to generate a dedicated deserializer because the member xml name is different
// from the name of the target shape
explicitBoundStructureDeserializer(ctx, shape)
} else {
super.payloadDeserializer(ctx, shape)
super.payloadDeserializer(ctx, shape, members)
}
}

Expand Down Expand Up @@ -116,13 +120,17 @@ class RestXmlSerializerGenerator(
writer: KotlinWriter
): XmlSerdeDescriptorGenerator = RestXmlSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members)

override fun payloadSerializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
override fun payloadSerializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape>?
): Symbol {
return if (shape.hasTrait<XmlNameTrait>() && shape is MemberShape) {
// can't delegate, have to generate a dedicated serializer because the member xml name is different
// from the name of the target shape
explicitBoundStructureSerializer(ctx, shape)
} else {
super.payloadSerializer(ctx, shape)
super.payloadSerializer(ctx, shape, members)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,14 @@ abstract class AbstractQueryFormUrlSerializerGenerator(
writer.write("return serializer.toByteArray()")
}

private fun documentSerializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
private fun documentSerializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape> = shape.members()
): Symbol {
val symbol = ctx.symbolProvider.toSymbol(shape)
return symbol.documentSerializer(ctx.settings) { writer ->
val fnName = symbol.documentSerializerName()
writer.openBlock("internal fun #L(serializer: #T, input: #T) {", fnName, RuntimeTypes.Serde.Serializer, symbol)
return shape.documentSerializer(ctx.settings, symbol, members) { writer ->
writer.openBlock("internal fun #identifier.name:L(serializer: #T, input: #T) {", RuntimeTypes.Serde.Serializer, symbol)
.call {
renderSerializerBody(ctx, shape, shape.members().toList(), writer)
}
Expand All @@ -159,15 +162,20 @@ abstract class AbstractQueryFormUrlSerializerGenerator(
}
}

override fun payloadSerializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
override fun payloadSerializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape>?
): Symbol {
// re-use document serializer (for the target shape!)
val target = shape.targetOrSelf(ctx.model)
val symbol = ctx.symbolProvider.toSymbol(shape)
val serializeFn = documentSerializer(ctx, target)
val fnName = symbol.payloadSerializerName()
return symbol.payloadSerializer(ctx.settings) { writer ->
val forMembers = members ?: target.members()

val serializeFn = documentSerializer(ctx, target, forMembers)
return target.payloadSerializer(ctx.settings, symbol, forMembers) { writer ->
addNestedDocumentSerializers(ctx, target, writer)
writer.withBlock("internal fun #L(input: #T): ByteArray {", "}", fnName, symbol) {
writer.withBlock("internal fun #identifier.name:L(input: #T): ByteArray {", "}", symbol) {
write("val serializer = #T()", RuntimeTypes.Serde.SerdeFormUrl.FormUrlSerializer)
write("#T(serializer, input)", serializeFn)
write("return serializer.toByteArray()")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class EventStreamParserGenerator(
* ```
*/
fun responseHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol =
// FIXME - don't use the body deserializer name since we may need to re-use it (albeit with a different signature, we should still be more explicit than this)
op.bodyDeserializer(ctx.settings) { writer ->
val outputSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape<StructureShape>(op.output.get()))
// we have access to the builder for the output type and the full HttpBody
Expand Down Expand Up @@ -110,14 +109,15 @@ class EventStreamParserGenerator(

val eventHeaderBindings = variant.members().filter { it.hasTrait<EventHeaderTrait>() }
val eventPayloadBinding = variant.members().firstOrNull { it.hasTrait<EventPayloadTrait>() }
val unbound = variant.members().filterNot { it.hasTrait<EventHeaderTrait>() || it.hasTrait<EventPayloadTrait>() }

if (eventHeaderBindings.isEmpty() && eventPayloadBinding == null) {
// the entire variant can be deserialized from the payload
val payloadDeserializeFn = sdg.payloadDeserializer(ctx, member)
writer.write("val e = #T(message.payload)", payloadDeserializeFn)
} else {
val variantSymbol = ctx.symbolProvider.toSymbol(variant)
writer.write("val builder = #T.Builder()", variantSymbol)
writer.write("val eb = #T.Builder()", variantSymbol)

// render members bound to header
eventHeaderBindings.forEach { hdrBinding ->
Expand All @@ -142,21 +142,20 @@ class EventStreamParserGenerator(
} else {
""
}
writer.write("builder.#L = message.headers.find { it.name == #S }?.value?.#T()$defaultValuePostfix", hdrBinding.defaultName(), hdrBinding.memberName, conversionFn)
writer.write("eb.#L = message.headers.find { it.name == #S }?.value?.#T()$defaultValuePostfix", hdrBinding.defaultName(), hdrBinding.memberName, conversionFn)
}

if (eventPayloadBinding != null) {
renderDeserializeExplicitEventPayloadMember(ctx, eventPayloadBinding, writer)
} else {
val members = variant.members().filterNot { it.hasTrait<EventHeaderTrait>() }
if (members.isNotEmpty()) {
if (unbound.isNotEmpty()) {
// all remaining members are bound to payload (but not explicitly bound via @eventPayload)
// use the operation body deserializer
TODO("render unbound event stream payload members")
// FIXME - this would require us to generate a "partial" deserializer like we do for where the builder is passed in
TODO("support for unbound event stream members is not implemented")
}
}

writer.write("val e = builder.build()")
writer.write("val e = eb.build()")
}

writer.write("#T.#L(e)", unionSymbol, member.unionVariantName())
Expand All @@ -167,15 +166,15 @@ class EventStreamParserGenerator(
member: MemberShape,
writer: KotlinWriter
) {
// FIXME - check content type for blob and string
// TODO - check content type for blob and string
// structure > :test(member > :test(blob, string, structure, union))
val target = ctx.model.expectShape(member.target)
when (target.type) {
ShapeType.BLOB -> writer.write("builder.#L = message.payload", member.defaultName())
ShapeType.STRING -> writer.write("builder.#L = message.payload?.decodeToString()", member.defaultName())
ShapeType.BLOB -> writer.write("eb.#L = message.payload", member.defaultName())
ShapeType.STRING -> writer.write("eb.#L = message.payload.decodeToString()", member.defaultName())
ShapeType.STRUCTURE, ShapeType.UNION -> {
val payloadDeserializeFn = sdg.payloadDeserializer(ctx, member)
writer.write("builder.#L = #T(message.payload)", member.defaultName(), payloadDeserializeFn)
writer.write("eb.#L = #T(message.payload)", member.defaultName(), payloadDeserializeFn)
}
else -> throw CodegenException("unsupported shape type `${target.type}` for target: $target; expected blob, string, structure, or union for eventPayload member: $member")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class EventStreamSerializerGenerator(
* ```
*/
fun requestHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol =
// FIXME - don't use the body serializer name since we may need to re-use it (albeit with a different signature, we should still be more explicit than this)
op.bodySerializer(ctx.settings) { writer ->
val inputSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape<StructureShape>(op.input.get()))
writer.withBlock(
Expand Down Expand Up @@ -89,7 +88,7 @@ class EventStreamSerializerGenerator(
definitionFile = "${op.serializerName()}.kt"

renderBy = { writer ->
// FIXME - make internal and share across operations?
// TODO - make internal and share across operations?
writer.withBlock(
"private fun #L(input: #T): #T = #T {", "}",
fnName,
Expand All @@ -108,11 +107,21 @@ class EventStreamSerializerGenerator(
member.unionVariantName()
) {
addStringHeader(":event-type", member.memberName)
val target = ctx.model.expectShape(member.target)
target.members().forEach { targetMember ->
when {
targetMember.hasTrait<EventHeaderTrait>() -> renderSerializeEventHeader(ctx, targetMember, writer)
targetMember.hasTrait<EventPayloadTrait>() -> renderSerializeEventPayload(ctx, targetMember, writer)
val variant = ctx.model.expectShape(member.target)

val eventHeaderBindings = variant.members().filter { it.hasTrait<EventHeaderTrait>() }
val eventPayloadBinding = variant.members().firstOrNull { it.hasTrait<EventPayloadTrait>() }
val unbound = variant.members().filterNot { it.hasTrait<EventHeaderTrait>() || it.hasTrait<EventPayloadTrait>() }

eventHeaderBindings.forEach { renderSerializeEventHeader(ctx, it, writer) }

when {
eventPayloadBinding != null -> renderSerializeEventPayload(ctx, eventPayloadBinding, writer)
unbound.isNotEmpty() -> {
writer.addStringHeader(":content-type", payloadContentType)
// get a payload serializer for the given members of the variant
val serializeFn = sdg.payloadSerializer(ctx, variant, unbound)
writer.write("payload = #T(input.value)", serializeFn)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ class AwsHttpBindingProtocolGeneratorTest {
error("Unneeded for test")
}

override fun payloadDeserializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
override fun payloadDeserializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape>?
): Symbol {
error("Unneeded for test")
}
}
Expand All @@ -112,7 +116,11 @@ class AwsHttpBindingProtocolGeneratorTest {
error("Unneeded for test")
}

override fun payloadSerializer(ctx: ProtocolGenerator.GenerationContext, shape: Shape): Symbol {
override fun payloadSerializer(
ctx: ProtocolGenerator.GenerationContext,
shape: Shape,
members: Collection<MemberShape>?
): Symbol {
error("Unneeded for test")
}
}
Expand Down
3 changes: 3 additions & 0 deletions gradle/jvm.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jvmTest {
testLogging {
events("passed", "skipped", "failed")
showStandardStreams = true
showStackTraces = true
showExceptions = true
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}

useJUnitPlatform()
Expand Down
4 changes: 2 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ include(":aws-runtime:protocols:aws-json-protocols")
include(":aws-runtime:protocols:aws-xml-protocols")
include(":aws-runtime:protocols:aws-event-stream")
include(":aws-runtime:crt-util")
// include(":tests")
// include(":tests:codegen:event-stream")
include(":tests")
include(":tests:codegen:event-stream")

// generated services
fun File.isServiceDir(): Boolean {
Expand Down
Loading