-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for
smithy.protocols#rpcv2Cbor
protocol (#1103)
- Loading branch information
Showing
82 changed files
with
6,704 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"id": "4f6cf597-a267-4bca-a8b9-98aa055a9a72", | ||
"type": "feature", | ||
"description": "Add support for `smithy.protocols#rpcv2Cbor` protocol", | ||
"issues": [ | ||
"https://github.com/awslabs/aws-sdk-kotlin/issues/1302" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"id": "d079d678-08d3-437a-b638-2ef11e339938", | ||
"type": "feature", | ||
"description": "Add support for prioritized protocol resolution", | ||
"issues": [ | ||
"https://github.com/smithy-lang/smithy-kotlin/issues/843" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,5 +28,6 @@ class SdkProtocolGeneratorSupplier : KotlinIntegration { | |
RestXml(), | ||
AwsQuery(), | ||
Ec2Query(), | ||
RpcV2Cbor(), | ||
) | ||
} |
162 changes: 162 additions & 0 deletions
162
...-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RpcV2Cbor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package software.amazon.smithy.kotlin.codegen.aws.protocols | ||
|
||
import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator | ||
import software.amazon.smithy.kotlin.codegen.aws.protocols.core.StaticHttpBindingResolver | ||
import software.amazon.smithy.kotlin.codegen.core.KotlinWriter | ||
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes | ||
import software.amazon.smithy.kotlin.codegen.model.* | ||
import software.amazon.smithy.kotlin.codegen.model.traits.SyntheticClone | ||
import software.amazon.smithy.kotlin.codegen.rendering.protocol.* | ||
import software.amazon.smithy.kotlin.codegen.rendering.serde.CborParserGenerator | ||
import software.amazon.smithy.kotlin.codegen.rendering.serde.CborSerializerGenerator | ||
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator | ||
import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataSerializerGenerator | ||
import software.amazon.smithy.model.Model | ||
import software.amazon.smithy.model.knowledge.HttpBinding | ||
import software.amazon.smithy.model.pattern.UriPattern | ||
import software.amazon.smithy.model.shapes.OperationShape | ||
import software.amazon.smithy.model.shapes.ServiceShape | ||
import software.amazon.smithy.model.shapes.ShapeId | ||
import software.amazon.smithy.model.traits.HttpTrait | ||
import software.amazon.smithy.model.traits.TimestampFormatTrait | ||
import software.amazon.smithy.model.traits.UnitTypeTrait | ||
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait | ||
|
||
class RpcV2Cbor : AwsHttpBindingProtocolGenerator() { | ||
override val protocol: ShapeId = Rpcv2CborTrait.ID | ||
|
||
// TODO Timestamp format is not used in RpcV2Cbor since it's a binary protocol. We seem to be missing an abstraction | ||
// between text-based and binary-based protocols | ||
override val defaultTimestampFormat = TimestampFormatTrait.Format.UNKNOWN | ||
|
||
override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = | ||
RpcV2CborHttpBindingResolver(model, serviceShape) | ||
|
||
override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = | ||
CborSerializerGenerator(this) | ||
|
||
override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = | ||
CborParserGenerator(this) | ||
|
||
override fun renderDeserializeErrorDetails(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { | ||
writer.write("#T.deserialize(payload)", RuntimeTypes.SmithyRpcV2Protocols.Cbor.RpcV2CborErrorDeserializer) | ||
} | ||
|
||
override fun getDefaultHttpMiddleware(ctx: ProtocolGenerator.GenerationContext): List<ProtocolMiddleware> { | ||
// Every request MUST contain a `smithy-protocol` header with the value of `rpc-v2-cbor` | ||
val smithyProtocolHeaderMiddleware = MutateHeadersMiddleware(overrideHeaders = mapOf("smithy-protocol" to "rpc-v2-cbor")) | ||
|
||
// Every response MUST contain the same `smithy-protocol` header, otherwise it's considered invalid | ||
val validateSmithyProtocolHeaderMiddleware = object : ProtocolMiddleware { | ||
override val name: String = "RpcV2CborValidateSmithyProtocolResponseHeader" | ||
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { | ||
val interceptorSymbol = RuntimeTypes.SmithyRpcV2Protocols.Cbor.RpcV2CborSmithyProtocolResponseHeaderInterceptor | ||
writer.write("op.interceptors.add(#T)", interceptorSymbol) | ||
} | ||
} | ||
|
||
// Requests with event stream responses MUST include an `Accept` header set to the value `application/vnd.amazon.eventstream` | ||
val eventStreamsAcceptHeaderMiddleware = object : ProtocolMiddleware { | ||
private val mutateHeadersMiddleware = MutateHeadersMiddleware(extraHeaders = mapOf("Accept" to "application/vnd.amazon.eventstream")) | ||
|
||
override fun isEnabledFor(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Boolean = op.isOutputEventStream(ctx.model) | ||
override val name: String = "RpcV2CborEventStreamsAcceptHeaderMiddleware" | ||
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) = mutateHeadersMiddleware.render(ctx, op, writer) | ||
} | ||
|
||
// Emit a metric to track usage of RpcV2Cbor | ||
val businessMetricsMiddleware = object : ProtocolMiddleware { | ||
override val name: String = "RpcV2CborBusinessMetricsMiddleware" | ||
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { | ||
writer.write("op.context.#T(#T.PROTOCOL_RPC_V2_CBOR)", RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, RuntimeTypes.Core.BusinessMetrics.SmithyBusinessMetric) | ||
} | ||
} | ||
|
||
return super.getDefaultHttpMiddleware(ctx) + listOf( | ||
smithyProtocolHeaderMiddleware, | ||
validateSmithyProtocolHeaderMiddleware, | ||
eventStreamsAcceptHeaderMiddleware, | ||
businessMetricsMiddleware, | ||
) | ||
} | ||
|
||
/** | ||
* Exact copy of [HttpBindingProtocolGenerator.renderSerializeHttpBody] but with a custom | ||
* [OperationShape.hasHttpBody] function to handle protocol-specific serialization rules. | ||
*/ | ||
override fun renderSerializeHttpBody( | ||
ctx: ProtocolGenerator.GenerationContext, | ||
op: OperationShape, | ||
writer: KotlinWriter, | ||
) { | ||
val resolver = getProtocolHttpBindingResolver(ctx.model, ctx.service) | ||
if (!op.hasHttpBody(ctx)) return | ||
|
||
// payload member(s) | ||
val requestBindings = resolver.requestBindings(op) | ||
val httpPayload = requestBindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD } | ||
if (httpPayload != null) { | ||
renderExplicitHttpPayloadSerializer(ctx, httpPayload, writer) | ||
} else { | ||
val documentMembers = requestBindings.filterDocumentBoundMembers() | ||
// Unbound document members that should be serialized into the document format for the protocol. | ||
// delegate to the generate operation body serializer function | ||
val sdg = structuredDataSerializer(ctx) | ||
val opBodySerializerFn = sdg.operationSerializer(ctx, op, documentMembers) | ||
writer.write("builder.body = #T(context, input)", opBodySerializerFn) | ||
} | ||
renderContentTypeHeader(ctx, op, writer, resolver) | ||
} | ||
|
||
/** | ||
* @return whether the operation input does _not_ target the unit shape ([UnitTypeTrait.UNIT]) | ||
*/ | ||
private fun OperationShape.hasHttpBody(ctx: ProtocolGenerator.GenerationContext): Boolean { | ||
val input = ctx.model.expectShape(inputShape).targetOrSelf(ctx.model).let { | ||
// If the input has been synthetically cloned from the original (most likely), | ||
// pull the archetype and check _that_ | ||
it.getTrait<SyntheticClone>()?.let { clone -> | ||
ctx.model.expectShape(clone.archetype).targetOrSelf(ctx.model) | ||
} ?: it | ||
} | ||
|
||
return input.id != UnitTypeTrait.UNIT | ||
} | ||
|
||
override fun renderContentTypeHeader( | ||
ctx: ProtocolGenerator.GenerationContext, | ||
op: OperationShape, | ||
writer: KotlinWriter, | ||
resolver: HttpBindingResolver, | ||
) { | ||
writer.write("builder.headers.setMissing(\"Content-Type\", #S)", resolver.determineRequestContentType(op)) | ||
} | ||
|
||
class RpcV2CborHttpBindingResolver( | ||
model: Model, | ||
val serviceShape: ServiceShape, | ||
) : StaticHttpBindingResolver( | ||
model, | ||
serviceShape, | ||
HttpTrait.builder().code(200).method("POST").uri(UriPattern.parse("/")).build(), | ||
"application/cbor", | ||
TimestampFormatTrait.Format.UNKNOWN, | ||
) { | ||
|
||
override fun httpTrait(operationShape: OperationShape): HttpTrait = HttpTrait | ||
.builder() | ||
.code(200) | ||
.method("POST") | ||
.uri(UriPattern.parse("/service/${serviceShape.id.name}/operation/${operationShape.id.name}")) | ||
.build() | ||
|
||
override fun determineRequestContentType(operationShape: OperationShape): String = when { | ||
operationShape.isInputEventStream(model) -> "application/vnd.amazon.eventstream" | ||
else -> "application/cbor" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.