diff --git a/aws-runtime/aws-core/common/src/aws/sdk/kotlin/runtime/client/AwsClientOption.kt b/aws-runtime/aws-core/common/src/aws/sdk/kotlin/runtime/client/AwsClientOption.kt index 891e03e639d..c708d77f77f 100644 --- a/aws-runtime/aws-core/common/src/aws/sdk/kotlin/runtime/client/AwsClientOption.kt +++ b/aws-runtime/aws-core/common/src/aws/sdk/kotlin/runtime/client/AwsClientOption.kt @@ -14,9 +14,11 @@ import aws.smithy.kotlin.runtime.collections.AttributeKey public object AwsClientOption { /** * The AWS region the client should use. Note this is not always the same as [AwsSigningAttributes.SigningRegion] in - * the case of global services like IAM + * the case of global services like IAM. + * + * NOTE: Synonymous with [aws.smithy.kotlin.runtime.awsprotocol.AwsAttributes.Region] */ - public val Region: AttributeKey = AttributeKey("aws.sdk.kotlin#AwsRegion") + public val Region: AttributeKey = AttributeKey("aws.smithy.kotlin#AwsRegion") /** * The ID of the AWS account requests are routed to. diff --git a/codegen/aws-sdk-codegen/build.gradle.kts b/codegen/aws-sdk-codegen/build.gradle.kts index 12b8d63dff1..719643eabc8 100644 --- a/codegen/aws-sdk-codegen/build.gradle.kts +++ b/codegen/aws-sdk-codegen/build.gradle.kts @@ -20,10 +20,9 @@ group = "aws.sdk.kotlin" version = sdkVersion dependencies { - - api(project(":codegen:smithy-aws-kotlin-codegen")) implementation(libs.kotlin.stdlib.jdk8) api(libs.smithy.kotlin.codegen) + api(libs.smithy.aws.kotlin.codegen) api(libs.smithy.aws.traits) api(libs.smithy.aws.iam.traits) diff --git a/codegen/protocol-tests/build.gradle.kts b/codegen/protocol-tests/build.gradle.kts index 4c6eeabe829..f767e24df20 100644 --- a/codegen/protocol-tests/build.gradle.kts +++ b/codegen/protocol-tests/build.gradle.kts @@ -21,29 +21,15 @@ data class ProtocolTest(val projectionName: String, val serviceShapeId: String, // The following section exposes Smithy protocol test suites as gradle test targets // for the configured protocols in [enabledProtocols]. val enabledProtocols = listOf( - ProtocolTest("aws-ec2-query", "aws.protocoltests.ec2#AwsEc2"), - ProtocolTest("aws-json-10", "aws.protocoltests.json10#JsonRpc10"), - ProtocolTest("aws-json-11", "aws.protocoltests.json#JsonProtocol"), - ProtocolTest("aws-restjson", "aws.protocoltests.restjson#RestJson"), - ProtocolTest("aws-restxml", "aws.protocoltests.restxml#RestXml"), - ProtocolTest("aws-restxml-xmlns", "aws.protocoltests.restxml.xmlns#RestXmlWithNamespace"), - ProtocolTest("aws-query", "aws.protocoltests.query#AwsQuery"), - // service specific tests ProtocolTest("apigateway", "com.amazonaws.apigateway#BackplaneControlService"), ProtocolTest("glacier", "com.amazonaws.glacier#Glacier"), ProtocolTest("machinelearning", "com.amazonaws.machinelearning#AmazonML_20141212", sdkId = "Machine Learning"), - - // Custom hand written tests - ProtocolTest("error-correction-json", "aws.protocoltests.errorcorrection#RequiredValueJson"), - ProtocolTest("error-correction-xml", "aws.protocoltests.errorcorrection#RequiredValueXml"), ) smithyBuild { enabledProtocols.forEach { test -> projections.register(test.projectionName) { - imports = listOf(file("model").absolutePath) - transforms = listOf( """ { diff --git a/codegen/protocol-tests/model/error-correction-tests.smithy b/codegen/protocol-tests/model/error-correction-tests.smithy deleted file mode 100644 index 74e614bdc5a..00000000000 --- a/codegen/protocol-tests/model/error-correction-tests.smithy +++ /dev/null @@ -1,156 +0,0 @@ -$version: "2.0" - -namespace aws.protocoltests.errorcorrection - -use aws.api#service -use aws.protocols#awsJson1_0 -use aws.protocols#restXml -use smithy.test#httpResponseTests - -@service(sdkId: "Error Correction Json") -@awsJson1_0 -service RequiredValueJson { - operations: [SayHello], - version: "1" -} - - -@service(sdkId: "Error Correction Xml") -@restXml -service RequiredValueXml { - operations: [SayHelloXml], - version: "1" -} - -@error("client") -structure Error { - @required - requestId: String - - @required - message: String -} - -@http(method: "POST", uri: "/") -operation SayHello { output: TestOutputDocument, errors: [Error] } - -@http(method: "POST", uri: "/") -operation SayHelloXml { output: TestOutput, errors: [Error] } - -structure TestOutputDocument with [TestStruct] { innerField: Nested, @required document: Document } -structure TestOutput with [TestStruct] { innerField: Nested } - -@mixin -structure TestStruct { - @required - foo: String, - - @required - byteValue: Byte, - - @required - intValue: Integer, - - @required - listValue: StringList, - - @required - mapValue: ListMap, - - @required - nestedListValue: NestedList - - @required - nested: Nested - - @required - blob: Blob - - @required - enum: MyEnum - - @required - union: MyUnion - - notRequired: String - - @required - timestampValue: Timestamp -} - -enum MyEnum { - A, - B, - C -} - -union MyUnion { - A: Integer, - B: String, - C: Unit -} - -structure Nested { - @required - a: String -} - -list StringList { - member: String -} - -list NestedList { - member: StringList -} - -map ListMap { - key: String, - value: StringList -} - -// NOTE: there is no way to model enum or union defaults in an `httpResponseTest` because the default is the generated -// "SdkUnknown" variant. -apply SayHello @httpResponseTests([ - { - id: "error_recovery_json", - protocol: awsJson1_0, - params: { - union: { A: 5 }, - enum: "A", - foo: "", - byteValue: 0, - intValue: 0, - blob: "", - listValue: [], - mapValue: {}, - nestedListValue: [], - document: null, - nested: null, - timestampValue: 0 - }, - code: 200, - body: "{\"union\": { \"A\": 5 }, \"enum\": \"A\" }" - } -]) - -apply SayHelloXml @httpResponseTests([ - { - id: "error_recovery_xml", - protocol: restXml, - params: { - union: { A: 5 }, - enum: "A", - foo: "", - byteValue: 0, - intValue: 0, - blob: "", - listValue: [], - mapValue: {}, - nestedListValue: [], - nested: null, - timestampValue: 0 - }, - code: 200, - body: "5A" - } -]) \ No newline at end of file diff --git a/codegen/smithy-aws-kotlin-codegen/build.gradle.kts b/codegen/smithy-aws-kotlin-codegen/build.gradle.kts deleted file mode 100644 index 60a6b6584e1..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/build.gradle.kts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -plugins { - alias(libs.plugins.kotlin.jvm) - jacoco -} - -val sdkVersion: String by project -description = "Codegen support for AWS protocols" -group = "software.amazon.smithy.kotlin" -version = sdkVersion - -dependencies { - - implementation(libs.kotlin.stdlib.jdk8) - api(libs.smithy.kotlin.codegen) - - api(libs.smithy.aws.traits) - api(libs.smithy.aws.iam.traits) - api(libs.smithy.aws.cloudformation.traits) - api(libs.smithy.protocol.test.traits) - implementation(libs.smithy.aws.endpoints) - - testImplementation(libs.junit.jupiter) - testImplementation(libs.junit.jupiter.params) - testImplementation(libs.kotest.assertions.core.jvm) - testImplementation(libs.kotlin.test.junit5) - testImplementation(libs.smithy.kotlin.codegen.testutils) - - testImplementation(libs.slf4j.api) - testImplementation(libs.slf4j.simple) - testImplementation(libs.kotlinx.serialization.json) -} - -tasks.withType { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) - } -} - -tasks.withType { - sourceCompatibility = JavaVersion.VERSION_17.toString() - targetCompatibility = JavaVersion.VERSION_17.toString() -} - -// Reusable license copySpec -val licenseSpec = copySpec { - from("${project.rootDir}/LICENSE") - from("${project.rootDir}/NOTICE") -} - -// Configure jars to include license related info -tasks.jar { - metaInf.with(licenseSpec) - inputs.property("moduleName", project.name) - manifest { - attributes["Automatic-Module-Name"] = project.name - } -} - -tasks.test { - useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - showStandardStreams = true - showStackTraces = true - showExceptions = true - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - } -} - -// Configure jacoco (code coverage) to generate an HTML report -tasks.jacocoTestReport { - reports { - xml.required.set(false) - csv.required.set(false) - html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco")) - } -} - -// Always run the jacoco test report after testing. -tasks["test"].finalizedBy(tasks["jacocoTestReport"]) - -val sourcesJar by tasks.creating(Jar::class) { - group = "publishing" - description = "Assembles Kotlin sources jar" - archiveClassifier.set("sources") - from(sourceSets.getByName("main").allSource) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/SdkProtocolGeneratorSupplier.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/SdkProtocolGeneratorSupplier.kt deleted file mode 100644 index 69ca17292c6..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/SdkProtocolGeneratorSupplier.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws - -import software.amazon.smithy.kotlin.codegen.aws.protocols.* -import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator - -/** - * Integration that registers protocol generators this package provides - */ -class SdkProtocolGeneratorSupplier : KotlinIntegration { - /** - * Gets the sort order of the customization from -128 to 127, with lowest - * executed first. - * - * @return Returns the sort order, defaults to -10. - */ - override val order: Byte = -10 - - override val protocolGenerators: List = - listOf( - AwsJson1_0(), - AwsJson1_1(), - RestJson1(), - RestXml(), - AwsQuery(), - Ec2Query(), - ) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/customization/RegionSupport.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/customization/RegionSupport.kt deleted file mode 100644 index 1aeef428abd..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/customization/RegionSupport.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.customization - -import software.amazon.smithy.aws.traits.ServiceTrait -import software.amazon.smithy.kotlin.codegen.KotlinSettings -import software.amazon.smithy.kotlin.codegen.core.CodegenContext -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.getContextValue -import software.amazon.smithy.kotlin.codegen.integration.AppendingSectionWriter -import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration -import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding -import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes -import software.amazon.smithy.kotlin.codegen.model.* -import software.amazon.smithy.kotlin.codegen.model.knowledge.AwsSignatureVersion4 -import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointCustomization -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpProtocolClientGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpProtocolUnitTestRequestGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.putIfAbsent -import software.amazon.smithy.kotlin.codegen.rendering.util.ConfigProperty -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.rulesengine.language.EndpointRuleSet -import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter - -/** - * Registers support for the concept of region on the service client config, endpoint builtins, etc. - * - * Region is enabled IFF sigv4(a) is enabled or an AWS SDK service is targeted - */ -class RegionSupport : KotlinIntegration { - companion object { - const val BUILTIN_NAME = "AWS::Region" - - val RegionProp: ConfigProperty = ConfigProperty { - name = "region" - symbol = KotlinTypes.String.toBuilder().nullable().build() - documentation = """ - The region to sign with and make requests to. - """.trimIndent() - } - } - - // Allow other integrations to customize the service config props, later integrations take precedence. - // This is used by AWS SDK codegen to customize the base class and documentation for this property - override val order: Byte = -50 - - override fun enabledForService(model: Model, settings: KotlinSettings): Boolean { - val service = model.expectShape(settings.service) - val supportsSigv4 = AwsSignatureVersion4.isSupportedAuthentication(model, service) - val hasRegionBuiltin = service.getEndpointRules()?.parameters?.find { it.isBuiltIn && it.builtIn.get() == BUILTIN_NAME } != null - val isAwsSdk = service.hasTrait() - return supportsSigv4 || hasRegionBuiltin || isAwsSdk - } - - override fun additionalServiceConfigProps(ctx: CodegenContext): List = listOf(RegionProp) - - override fun customizeEndpointResolution(ctx: ProtocolGenerator.GenerationContext): EndpointCustomization = - object : EndpointCustomization { - override fun renderBindEndpointBuiltins( - ctx: ProtocolGenerator.GenerationContext, - rules: EndpointRuleSet, - writer: KotlinWriter, - ) { - val builtins = rules.parameters?.toList()?.filter(Parameter::isBuiltIn) ?: return - builtins.forEach { - when (it.builtIn.get()) { - BUILTIN_NAME -> writer.write("#L = config.#L", it.defaultName(), RegionProp.propertyName) - } - } - } - } - override val sectionWriters: List - get() = listOf( - SectionWriterBinding(HttpProtocolUnitTestRequestGenerator.ConfigureServiceClient, renderHttpProtocolRequestTestConfigureServiceClient), - SectionWriterBinding(HttpProtocolClientGenerator.MergeServiceDefaults, renderRegionOperationContextDefault), - ) - - // sets a default region for protocol tests - private val renderHttpProtocolRequestTestConfigureServiceClient = AppendingSectionWriter { writer -> - // specify a default region - writer.write("region = #S", "us-east-1") - } - - // sets (initial) region/signing region in the execution context - private val renderRegionOperationContextDefault = AppendingSectionWriter { writer -> - val ctx = writer.getContextValue(HttpProtocolClientGenerator.MergeServiceDefaults.GenerationContext) - val isAwsSdk = ctx.service.hasTrait() - - if (isAwsSdk) { - val awsClientOption = buildSymbol { - name = "AwsClientOption" - namespace = "aws.sdk.kotlin.runtime.client" - } - writer.putIfAbsent(awsClientOption, "Region", nullable = true) - } - - writer.putIfAbsent( - RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes, - "SigningRegion", - "config.region", - nullable = true, - ) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/middleware/ClockSkew.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/middleware/ClockSkew.kt deleted file mode 100644 index ae71c8f5b36..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/middleware/ClockSkew.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.middleware - -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.integration.AppendingSectionWriter -import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration -import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding -import software.amazon.smithy.kotlin.codegen.rendering.ServiceClientGenerator - -/** - * Adds a section writer which applies an interceptor that detects and corrects clock skew - */ -class ClockSkew : KotlinIntegration { - override val sectionWriters: List - get() = listOf(SectionWriterBinding(ServiceClientGenerator.Sections.FinalizeConfig, clockSkewSectionWriter)) - - private val clockSkewSectionWriter = AppendingSectionWriter { writer -> - val interceptorSymbol = RuntimeTypes.AwsProtocolCore.ClockSkewInterceptor - writer.write("builder.config.interceptors.add(0, #T())", interceptorSymbol) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_0.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_0.kt deleted file mode 100644 index c43d68b2730..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_0.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.aws.traits.protocols.AwsJson1_0Trait -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonHttpBindingResolver -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonProtocolMiddleware -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonProtocolParserGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.JsonHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware -import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId - -/** - * Handles generating the aws.protocols#awsJson1_0 protocol for services. - * - * @inheritDoc - * @see AwsHttpBindingProtocolGenerator - */ -class AwsJson1_0 : JsonHttpBindingProtocolGenerator() { - override val protocol: ShapeId = AwsJson1_0Trait.ID - override val supportsJsonNameTrait: Boolean = false - - override fun getDefaultHttpMiddleware(ctx: ProtocolGenerator.GenerationContext): List { - val httpMiddleware = super.getDefaultHttpMiddleware(ctx) - val awsJsonMiddleware = listOf( - AwsJsonProtocolMiddleware(ctx.settings.service, "1.0"), - ) - - return httpMiddleware + awsJsonMiddleware - } - - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = - AwsJsonHttpBindingResolver(model, serviceShape, "application/x-amz-json-1.0") - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_1.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_1.kt deleted file mode 100644 index f112298eded..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsJson1_1.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.aws.traits.protocols.AwsJson1_1Trait -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonHttpBindingResolver -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonProtocolMiddleware -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonProtocolParserGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.JsonHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware -import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId - -/** - * Handles generating the aws.protocols#awsJson1_1 protocol for services. - * - * @inheritDoc - * @see AwsHttpBindingProtocolGenerator - */ -class AwsJson1_1 : JsonHttpBindingProtocolGenerator() { - override val protocol: ShapeId = AwsJson1_1Trait.ID - override val supportsJsonNameTrait: Boolean = false - - override fun getDefaultHttpMiddleware(ctx: ProtocolGenerator.GenerationContext): List { - val httpMiddleware = super.getDefaultHttpMiddleware(ctx) - val awsJsonMiddleware = listOf( - AwsJsonProtocolMiddleware(ctx.settings.service, "1.1"), - ) - - return httpMiddleware + awsJsonMiddleware - } - - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = - AwsJsonHttpBindingResolver(model, serviceShape, "application/x-amz-json-1.1") - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsQuery.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsQuery.kt deleted file mode 100644 index 027e9050f30..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/AwsQuery.kt +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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.aws.traits.protocols.AwsQueryErrorTrait -import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AbstractQueryFormUrlSerializerGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.QueryHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.formurl.QuerySerdeFormUrlDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.core.* -import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes -import software.amazon.smithy.kotlin.codegen.model.* -import software.amazon.smithy.kotlin.codegen.rendering.protocol.* -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.* - -/** - * Handles generating the aws.protocols#awsQuery protocol for services. - * - * @inheritDoc - * @see AwsHttpBindingProtocolGenerator - */ -class AwsQuery : QueryHttpBindingProtocolGenerator() { - override val protocol: ShapeId = AwsQueryTrait.ID - - override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = - AwsQuerySerializerGenerator(this) - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - AwsQueryXmlParserGenerator(this) - - override fun getErrorCode(ctx: ProtocolGenerator.GenerationContext, errShapeId: ShapeId): String { - val errShape = ctx.model.expectShape(errShapeId) - return errShape.getTrait()?.code ?: errShape.id.name - } - - override fun renderDeserializeErrorDetails( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - writer.write("""checkNotNull(payload){ "unable to parse error from empty response" }""") - writer.write("#T(payload)", RuntimeTypes.AwsXmlProtocols.parseRestXmlErrorResponse) - } -} - -private class AwsQuerySerdeFormUrlDescriptorGenerator( - ctx: RenderingContext, - memberShapes: List? = null, -) : QuerySerdeFormUrlDescriptorGenerator(ctx, memberShapes) { - /** - * The serialized name for a shape. See - * [AWS query protocol](https://awslabs.github.io/smithy/1.0/spec/aws/aws-query-protocol.html#query-key-resolution) - * for more information. - */ - override val objectSerialName: String - get() = objectShape.getTrait()?.value ?: super.objectSerialName - - override fun getMemberSerialNameOverride(member: MemberShape): String? = member.getTrait()?.value - - override fun isMemberFlattened(member: MemberShape, targetShape: Shape): Boolean = - member.hasTrait() -} - -private class AwsQuerySerializerGenerator( - private val protocolGenerator: AwsQuery, -) : AbstractQueryFormUrlSerializerGenerator(protocolGenerator, protocolGenerator.defaultTimestampFormat) { - override fun descriptorGenerator( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ): FormUrlSerdeDescriptorGenerator = AwsQuerySerdeFormUrlDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members) -} - -private class AwsQueryXmlParserGenerator( - protocolGenerator: AwsQuery, -) : XmlParserGenerator(protocolGenerator.defaultTimestampFormat) { - - /** - * Unwraps the response body as specified by - * https://awslabs.github.io/smithy/1.0/spec/aws/aws-query-protocol.html#response-serialization so that the - * deserializer is in the correct state. - * - * ``` - * - * - * <-- SAME AS REST XML --> - * - * - * ``` - */ - override fun unwrapOperationBody( - ctx: ProtocolGenerator.GenerationContext, - serdeCtx: SerdeCtx, - op: OperationShape, - writer: KotlinWriter, - ): SerdeCtx { - val operationName = op.id.getName(ctx.service) - - val unwrapAwsQueryOperation = buildSymbol { - name = "unwrapAwsQueryResponse" - namespace = ctx.settings.pkg.serde - definitionFile = "AwsQueryUtil.kt" - renderBy = { writer -> - - writer.withBlock( - "internal fun $name(root: #1T, operationName: #2T): #1T {", - "}", - RuntimeTypes.Serde.SerdeXml.XmlTagReader, - KotlinTypes.String, - ) { - write("val responseWrapperName = \"\${operationName}Response\"") - write("val resultWrapperName = \"\${operationName}Result\"") - withBlock( - "if (root.tagName != responseWrapperName) {", - "}", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid root, expected \$responseWrapperName; found `\${root.tag}`") - } - - write("val resultTag = ${serdeCtx.tagReader}.nextTag()") - withBlock( - "if (resultTag == null || resultTag.tagName != resultWrapperName) {", - "}", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid result, expected \$resultWrapperName; found `\${resultTag?.tag}`") - } - - write("return resultTag") - } - } - } - - writer.write("val unwrapped = #T(#L, #S)", unwrapAwsQueryOperation, serdeCtx.tagReader, operationName) - - return SerdeCtx("unwrapped") - } - - override fun unwrapOperationError( - ctx: ProtocolGenerator.GenerationContext, - serdeCtx: SerdeCtx, - errorShape: StructureShape, - writer: KotlinWriter, - ): SerdeCtx { - writer.write("val errReader = #T(${serdeCtx.tagReader})", RestXmlErrors.wrappedErrorResponseDeserializer(ctx)) - return SerdeCtx("errReader") - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/Ec2Query.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/Ec2Query.kt deleted file mode 100644 index 65751cefad6..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/Ec2Query.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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.aws.traits.protocols.Ec2QueryNameTrait -import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AbstractQueryFormUrlSerializerGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.QueryHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.formurl.QuerySerdeFormUrlDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RenderingContext -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.withBlock -import software.amazon.smithy.kotlin.codegen.model.buildSymbol -import software.amazon.smithy.kotlin.codegen.model.getTrait -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.toRenderingContext -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.XmlNameTrait - -/** - * Handles generating the aws.protocols#ec2Query protocol for services. - */ -class Ec2Query : QueryHttpBindingProtocolGenerator() { - override val protocol: ShapeId = Ec2QueryTrait.ID - - override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = - Ec2QuerySerializerGenerator(this) - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - Ec2QueryParserGenerator(this) - - override fun renderDeserializeErrorDetails( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - writer.write("""checkNotNull(payload){ "unable to parse error from empty response" }""") - writer.write("#T(payload)", RuntimeTypes.AwsXmlProtocols.parseEc2QueryErrorResponse) - } -} - -private class Ec2QuerySerdeFormUrlDescriptorGenerator( - ctx: RenderingContext, - memberShapes: List? = null, -) : QuerySerdeFormUrlDescriptorGenerator(ctx, memberShapes) { - /** - * The serialized name for a shape. See - * [EC2 query protocol](https://awslabs.github.io/smithy/1.0/spec/aws/aws-ec2-query-protocol.html#query-key-resolution) - * for more information. - */ - override val objectSerialName: String - get() = - objectShape.getTrait()?.value - ?: objectShape.getTrait()?.value?.replaceFirstChar(Char::uppercaseChar) - ?: super.objectSerialName - - override fun getMemberSerialNameOverride(member: MemberShape): String? = - member.getTrait()?.value - ?: member.getTrait()?.value?.replaceFirstChar(Char::uppercaseChar) - ?: if (member.memberName.firstOrNull()?.isUpperCase() == false) { - member.memberName.replaceFirstChar(Char::uppercaseChar) - } else { - null - } - - override fun isMemberFlattened(member: MemberShape, targetShape: Shape): Boolean = - targetShape.type == ShapeType.LIST -} - -private class Ec2QuerySerializerGenerator( - private val protocolGenerator: Ec2Query, -) : AbstractQueryFormUrlSerializerGenerator(protocolGenerator, protocolGenerator.defaultTimestampFormat) { - - override fun descriptorGenerator( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ): FormUrlSerdeDescriptorGenerator = Ec2QuerySerdeFormUrlDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members) -} - -private class Ec2QueryParserGenerator( - protocolGenerator: Ec2Query, -) : XmlParserGenerator(protocolGenerator.defaultTimestampFormat) { - override fun unwrapOperationError( - ctx: ProtocolGenerator.GenerationContext, - serdeCtx: SerdeCtx, - errorShape: StructureShape, - writer: KotlinWriter, - ): SerdeCtx { - val unwrapFn = unwrapErrorResponse(ctx) - writer.write("val errReader = #T(${serdeCtx.tagReader})", unwrapFn) - return SerdeCtx("errReader") - } - - /** - * Error deserializer for a wrapped error response - * - * ``` - * - * - * - * <-- DATA -->> - * - * - * - * ``` - * - * See https://smithy.io/2.0/aws/protocols/aws-ec2-query-protocol.html#operation-error-serialization - */ - private fun unwrapErrorResponse(ctx: ProtocolGenerator.GenerationContext): Symbol = buildSymbol { - name = "unwrapXmlErrorResponse" - namespace = ctx.settings.pkg.serde - definitionFile = "XmlErrorUtils.kt" - renderBy = { writer -> - writer.dokka("Handle [wrapped](https://smithy.io/2.0/aws/protocols/aws-ec2-query-protocol.html#operation-error-serialization) error responses") - writer.withBlock( - "internal fun $name(root: #1T): #1T {", - "}", - RuntimeTypes.Serde.SerdeXml.XmlTagReader, - ) { - withBlock( - "if (root.tagName != #S) {", - "}", - "Response", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid root, expected ; found `\${root.tag}`") - } - - write("val errorsTag = root.nextTag()") - withBlock( - "if (errorsTag == null || errorsTag.tagName != #S) {", - "}", - "Errors", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid error, expected ; found `\${errorsTag?.tag}`") - } - - write("val errTag = errorsTag.nextTag()") - withBlock( - "if (errTag == null || errTag.tagName != #S) {", - "}", - "Error", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid error, expected ; found `\${errTag?.tag}`") - } - - write("return errTag") - } - } - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestJson1.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestJson1.kt deleted file mode 100644 index 1a64f47653a..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestJson1.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.aws.traits.protocols.RestJson1Trait -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.AwsJsonProtocolParserGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.json.JsonHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.defaultName -import software.amazon.smithy.kotlin.codegen.core.withBlock -import software.amazon.smithy.kotlin.codegen.rendering.protocol.* -import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.knowledge.HttpBinding -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.shapes.StructureShape - -/** - * Handles generating the aws.protocols#restJson1 protocol for services. - * - * @inheritDoc - * @see AwsHttpBindingProtocolGenerator - */ -class RestJson1 : JsonHttpBindingProtocolGenerator() { - - override val protocol: ShapeId = RestJson1Trait.ID - - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = - HttpTraitResolver(model, serviceShape, ProtocolContentTypes.consistent("application/json")) - - override fun renderSerializeHttpBody( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - super.renderSerializeHttpBody(ctx, op, writer) - - val resolver = getProtocolHttpBindingResolver(ctx.model, ctx.service) - if (!resolver.hasHttpBody(op)) return - - // restjson1 has some different semantics and expectations around empty structures bound via @httpPayload trait - // * empty structures get serialized to `{}` - // see: https://github.com/awslabs/smithy/pull/924 - val requestBindings = resolver.requestBindings(op) - val httpPayload = requestBindings.firstOrNull { it.location == HttpBinding.Location.PAYLOAD } - if (httpPayload != null) { - // explicit payload member as the sole payload - val memberName = httpPayload.member.defaultName() - val target = ctx.model.expectShape(httpPayload.member.target) - writer.withBlock("if (input.#L == null) {", "}", memberName) { - if (target is StructureShape) { - write("builder.body = #T.fromBytes(#S.encodeToByteArray())", RuntimeTypes.Http.HttpBody, "{}") - } - // Content-Type still needs to be set for non-structured payloads - // https://github.com/awslabs/smithy/blob/main/smithy-aws-protocol-tests/model/restJson1/http-content-type.smithy#L174 - write("builder.headers.setMissing(\"Content-Type\", #S)", resolver.determineRequestContentType(op)) - } - } - } - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - AwsJsonProtocolParserGenerator(this, supportsJsonNameTrait) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestXml.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestXml.kt deleted file mode 100644 index c7c834c3175..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/RestXml.kt +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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.aws.traits.protocols.RestXmlTrait -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.xml.RestXmlSerdeDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.withBlock -import software.amazon.smithy.kotlin.codegen.model.* -import software.amazon.smithy.kotlin.codegen.rendering.protocol.* -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.model.traits.XmlNameTrait -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract - -/** - * Handles generating the aws.protocols#restJson1 protocol for services. - * - * @inheritDoc - * @see AwsHttpBindingProtocolGenerator - */ -open class RestXml : AwsHttpBindingProtocolGenerator() { - - override val protocol: ShapeId = RestXmlTrait.ID - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - - // See https://awslabs.github.io/smithy/1.0/spec/aws/aws-restxml-protocol.html#content-type - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = - HttpTraitResolver(model, serviceShape, ProtocolContentTypes.consistent("application/xml")) - - // See: https://github.com/awslabs/aws-sdk-kotlin/issues/1050 - override fun renderContentTypeHeader( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - resolver: HttpBindingResolver, - ) { - if (op.payloadIsUnionShape(ctx.model)) { - writer.write("builder.headers.setMissing(\"Content-Type\", #S)", resolver.determineRequestContentType(op)) - } else { - super.renderContentTypeHeader(ctx, op, writer, resolver) - } - } - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - RestXmlParserGenerator(this) - - override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = - RestXmlSerializerGenerator(this, defaultTimestampFormat) - - override fun renderDeserializeErrorDetails( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - writer.write("""checkNotNull(payload){ "unable to parse error from empty response" }""") - writer.write("#T(payload)", RuntimeTypes.AwsXmlProtocols.parseRestXmlErrorResponse) - } -} - -class RestXmlParserGenerator( - protocolGenerator: RestXml, -) : XmlParserGenerator(protocolGenerator.defaultTimestampFormat) { - - override fun payloadDeserializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection?, - ): Symbol = when { - // can't delegate, have to generate a dedicated deserializer because the member xml name is different - // from the name of the target shape - isXmlNamedMemberShape(shape) -> explicitBoundStructureDeserializer(ctx, shape) - else -> super.payloadDeserializer(ctx, shape, members) - } - - private fun explicitBoundStructureDeserializer( - ctx: ProtocolGenerator.GenerationContext, - boundMember: MemberShape, - ): Symbol { - val memberSymbol = ctx.symbolProvider.toSymbol(boundMember) - val targetShape = ctx.model.expectShape(boundMember.target) - - val xmlNameTrait = boundMember.expectTrait() - val copyWithMemberTraits = targetShape.toBuilder() - .removeTrait(XmlNameTrait.ID) - .addTrait(xmlNameTrait) - .build() - - return buildSymbol { - val xmlName = xmlNameTrait.value.replaceFirstChar(Char::uppercase) - name = "deserialize${memberSymbol.name}PayloadWithXmlName$xmlName" - namespace = ctx.settings.pkg.serde - definitionFile = "${memberSymbol.name}PayloadDeserializer.kt" - renderBy = { writer -> - addNestedDocumentDeserializers(ctx, targetShape, writer) - writer.dokka("Payload deserializer for ${memberSymbol.name} with a different XML name trait (${xmlNameTrait.value})") - writer.withBlock("internal fun $name(payload: ByteArray): #T {", "}", memberSymbol) { - writer.write("val root = #T(payload)", RuntimeTypes.Serde.SerdeXml.xmlRootTagReader) - val serdeCtx = SerdeCtx("root") - write("val builder = #T.Builder()", memberSymbol) - renderDeserializerBody(ctx, serdeCtx, copyWithMemberTraits, targetShape.members().toList(), writer) - write("return builder.build()") - } - } - } - } - - override fun unwrapOperationError( - ctx: ProtocolGenerator.GenerationContext, - serdeCtx: SerdeCtx, - errorShape: StructureShape, - writer: KotlinWriter, - ): SerdeCtx { - val unwrapFn = when (ctx.service.getTrait()?.isNoErrorWrapping == true) { - true -> RestXmlErrors.unwrappedErrorResponseDeserializer(ctx) - false -> RestXmlErrors.wrappedErrorResponseDeserializer(ctx) - } - writer.write("val errReader = #T(${serdeCtx.tagReader})", unwrapFn) - return SerdeCtx("errReader") - } -} - -object RestXmlErrors { - - /** - * Error deserializer for a wrapped error response - * - * ``` - * - * - * <-- DATA -->> - * - * - * ``` - * - * See https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#error-response-serialization - */ - fun wrappedErrorResponseDeserializer(ctx: ProtocolGenerator.GenerationContext): Symbol = buildSymbol { - name = "unwrapWrappedXmlErrorResponse" - namespace = ctx.settings.pkg.serde - definitionFile = "XmlErrorUtils.kt" - renderBy = { writer -> - writer.dokka("Handle [wrapped](https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#error-response-serialization) error responses") - writer.withBlock( - "internal fun $name(root: #1T): #1T {", - "}", - RuntimeTypes.Serde.SerdeXml.XmlTagReader, - ) { - withBlock( - "if (root.tagName != #S) {", - "}", - "ErrorResponse", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid root, expected ; found `\${root.tag}`") - } - - write("val errTag = root.nextTag()") - withBlock( - "if (errTag == null || errTag.tagName != #S) {", - "}", - "Error", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid error, expected ; found `\${errTag?.tag}`") - } - - write("return errTag") - } - } - } - - /** - * Error deserializer for an unwrapped error response - * - * ``` - * - * <-- DATA -->> - * - * ``` - * - * See https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#error-response-serialization - */ - fun unwrappedErrorResponseDeserializer(ctx: ProtocolGenerator.GenerationContext): Symbol = buildSymbol { - name = "unwrapXmlErrorResponse" - namespace = ctx.settings.pkg.serde - definitionFile = "XmlErrorUtils.kt" - renderBy = { writer -> - writer.dokka("Handle [unwrapped](https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html#error-response-serialization) error responses (restXml.noErrorWrapping == true)") - writer.withBlock( - "internal fun $name(root: #1T): #1T {", - "}", - RuntimeTypes.Serde.SerdeXml.XmlTagReader, - ) { - withBlock( - "if (root.tagName != #S) {", - "}", - "Error", - ) { - write("throw #T(#S)", RuntimeTypes.Serde.DeserializationException, "invalid error, expected ; found `\${root.tag}`") - } - - write("return root") - } - } - } -} - -class RestXmlSerializerGenerator( - private val protocolGenerator: RestXml, - defaultTimestampFormat: TimestampFormatTrait.Format, -) : XmlSerializerGenerator(protocolGenerator, defaultTimestampFormat) { - - override fun descriptorGenerator( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ): XmlSerdeDescriptorGenerator = RestXmlSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members) - - override fun payloadSerializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection?, - ): Symbol = when { - // can't delegate, have to generate a dedicated serializer because the member xml name is different - // from the name of the target shape - isXmlNamedMemberShape(shape) -> explicitBoundStructureSerializer(ctx, shape) - else -> super.payloadSerializer(ctx, shape, members) - } - - // FIXME - private fun explicitBoundStructureSerializer( - ctx: ProtocolGenerator.GenerationContext, - boundMember: MemberShape, - ): Symbol { - val memberSymbol = ctx.symbolProvider.toSymbol(boundMember) - val targetShape = ctx.model.expectShape(boundMember.target) - - val xmlNameTrait = boundMember.expectTrait() - val copyWithMemberTraits = targetShape.toBuilder() - .removeTrait(XmlNameTrait.ID) - .addTrait(xmlNameTrait) - .build() - - // we need a unique function specific to this XmlName - return buildSymbol { - val xmlName = xmlNameTrait.value.replaceFirstChar(Char::uppercase) - name = "serialize${memberSymbol.name}PayloadWithXmlName$xmlName" - namespace = ctx.settings.pkg.serde - // TODO - it would be nice to just inline this into the operation file as a private function instead - // since that is the only place it should be accessed - definitionFile = "${memberSymbol.name}PayloadSerializer.kt" - renderBy = { writer -> - addNestedDocumentSerializers(ctx, targetShape, writer) - writer.dokka("Payload serializer for ${memberSymbol.name} with a different XML name trait (${xmlNameTrait.value})") - writer.withBlock("internal fun $name(input: #T): ByteArray {", "}", memberSymbol) { - write("val serializer = #T()", RuntimeTypes.Serde.SerdeXml.XmlSerializer) - renderSerializerBody(ctx, copyWithMemberTraits, targetShape.members().toList(), writer) - write("return serializer.toByteArray()") - } - } - } - } -} - -@OptIn(ExperimentalContracts::class) -private fun isXmlNamedMemberShape(shape: Shape): Boolean { - contract { - returns(true) implies (shape is MemberShape) - } - return shape.hasTrait() && shape is MemberShape -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGenerator.kt deleted file mode 100644 index af74367323f..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGenerator.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.protocols.core - -import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.aws.protocols.eventstream.EventStreamParserGenerator -import software.amazon.smithy.kotlin.codegen.aws.protocols.eventstream.EventStreamSerializerGenerator -import software.amazon.smithy.kotlin.codegen.core.* -import software.amazon.smithy.kotlin.codegen.integration.SectionId -import software.amazon.smithy.kotlin.codegen.integration.SectionKey -import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes -import software.amazon.smithy.kotlin.codegen.model.buildSymbol -import software.amazon.smithy.kotlin.codegen.model.hasTrait -import software.amazon.smithy.kotlin.codegen.rendering.ExceptionBaseClassGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.* -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId - -/** - * Base class for all AWS HTTP protocol generators - */ -abstract class AwsHttpBindingProtocolGenerator : HttpBindingProtocolGenerator() { - - object Sections { - object ProtocolErrorDeserialization : SectionId { - val Operation = SectionKey("Operation") - } - - object RenderThrowOperationError : SectionId { - val Context = SectionKey("Context") - val Operation = SectionKey("Operation") - } - } - - override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext) { - // The following can be used to generate only a specific test by name. - // val targetedTest = TestMemberDelta(setOf("RestJsonComplexErrorWithNoMessage"), TestContainmentMode.RUN_TESTS) - - val ignoredTests = TestMemberDelta( - setOf( - // This test requires populating blob members with a default value of "", which the sdk doesn't do - "AwsJson10ClientPopulatesDefaultValuesInInput", - ), - ) - - val requestTestBuilder = HttpProtocolUnitTestRequestGenerator.Builder() - val responseTestBuilder = HttpProtocolUnitTestResponseGenerator.Builder() - val errorTestBuilder = HttpProtocolUnitTestErrorGenerator.Builder() - - HttpProtocolTestGenerator( - ctx, - requestTestBuilder, - responseTestBuilder, - errorTestBuilder, - ignoredTests, - ).generateProtocolTests() - } - - /** - * Get the error "code" that uniquely identifies the AWS error. - */ - protected open fun getErrorCode(ctx: ProtocolGenerator.GenerationContext, errShapeId: ShapeId): String = errShapeId.name - - /** - * Render the code to parse the `ErrorDetails` from the HTTP response. - */ - abstract fun renderDeserializeErrorDetails(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) - - override fun eventStreamRequestHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol { - val resolver = getProtocolHttpBindingResolver(ctx.model, ctx.service) - val contentType = resolver.determineRequestContentType(op) ?: error("event streams must set a content-type") - val eventStreamSerializerGenerator = EventStreamSerializerGenerator(structuredDataSerializer(ctx), contentType) - return eventStreamSerializerGenerator.requestHandler(ctx, op) - } - - override fun eventStreamResponseHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol { - val eventStreamParserGenerator = EventStreamParserGenerator(ctx, structuredDataParser(ctx)) - return eventStreamParserGenerator.responseHandler(ctx, op) - } - - override fun operationErrorHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol = - op.errorHandler(ctx.settings) { writer -> - writer.withBlock( - "private suspend fun ${op.errorHandlerName()}(context: #T, call: #T): #Q {", - "}", - RuntimeTypes.Core.ExecutionContext, - RuntimeTypes.Http.HttpCall, - KotlinTypes.Nothing, - ) { - renderThrowOperationError(ctx, op, writer) - } - } - - private fun renderThrowOperationError( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - writer.declareSection( - Sections.RenderThrowOperationError, - mapOf( - Sections.RenderThrowOperationError.Context to ctx, - Sections.RenderThrowOperationError.Operation to op, - ), - ) { - val exceptionBaseSymbol = ExceptionBaseClassGenerator.baseExceptionSymbol(ctx.settings) - writer.write("val payload = call.response.body.#T()", RuntimeTypes.Http.readAll) - .write("val wrappedResponse = call.response.#T(payload)", RuntimeTypes.AwsProtocolCore.withPayload) - .write("val wrappedCall = call.copy(response = wrappedResponse)") - .write("") - .declareSection( - Sections.ProtocolErrorDeserialization, - mapOf( - Sections.ProtocolErrorDeserialization.Operation to op, - ), - ) - .write("val errorDetails = try {") - .indent() - .call { - renderDeserializeErrorDetails(ctx, op, writer) - } - .dedent() - .withBlock("} catch (ex: Exception) {", "}") { - withBlock("""throw #T("Failed to parse response as '${ctx.protocol.name}' error", ex).also {""", "}", exceptionBaseSymbol) { - write("#T(it, wrappedCall.response, null)", RuntimeTypes.AwsProtocolCore.setAseErrorMetadata) - } - } - .write("") - - if (ctx.service.hasTrait()) { - writer.write("var queryErrorDetails: #T? = null", RuntimeTypes.AwsProtocolCore.AwsQueryCompatibleErrorDetails) - writer.withBlock("call.response.headers[#T]?.let {", "}", RuntimeTypes.AwsProtocolCore.XAmznQueryErrorHeader) { - openBlock("queryErrorDetails = try {") - write("#T.parse(it)", RuntimeTypes.AwsProtocolCore.AwsQueryCompatibleErrorDetails) - closeAndOpenBlock("} catch (ex: Exception) {") - withBlock("""throw #T("Failed to parse awsQuery-compatible error", ex).also {""", "}", exceptionBaseSymbol) { - write("#T(it, wrappedResponse, errorDetails)", RuntimeTypes.AwsProtocolCore.setAseErrorMetadata) - } - closeBlock("}") - } - writer.write("") - } - - writer.withBlock("val ex = when(errorDetails.code) {", "}") { - op.errors.forEach { err -> - val errSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(err)) - val errDeserializerSymbol = buildSymbol { - name = "${errSymbol.name}Deserializer" - namespace = ctx.settings.pkg.serde - } - writer.write("#S -> #T().deserialize(context, wrappedCall)", getErrorCode(ctx, err), errDeserializerSymbol) - } - write("else -> #T(errorDetails.message)", exceptionBaseSymbol) - } - - writer.write("") - writer.write("#T(ex, wrappedResponse, errorDetails)", RuntimeTypes.AwsProtocolCore.setAseErrorMetadata) - if (ctx.service.hasTrait()) { - writer.write("queryErrorDetails?.let { #T(ex, it) }", RuntimeTypes.AwsProtocolCore.setAwsQueryCompatibleErrorMetadata) - } - - writer.write("throw ex") - } - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/QueryHttpBindingProtocolGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/QueryHttpBindingProtocolGenerator.kt deleted file mode 100644 index c79bc20bc15..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/QueryHttpBindingProtocolGenerator.kt +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.protocols.core - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.withBlock -import software.amazon.smithy.kotlin.codegen.model.expectShape -import software.amazon.smithy.kotlin.codegen.model.knowledge.SerdeIndex -import software.amazon.smithy.kotlin.codegen.model.targetOrSelf -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver -import software.amazon.smithy.kotlin.codegen.rendering.protocol.MutateHeadersMiddleware -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.pattern.UriPattern -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.HttpTrait -import software.amazon.smithy.model.traits.TimestampFormatTrait - -private const val QueryContentType: String = "application/x-www-form-urlencoded" - -abstract class QueryHttpBindingProtocolGenerator : AwsHttpBindingProtocolGenerator() { - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - - override fun getDefaultHttpMiddleware(ctx: ProtocolGenerator.GenerationContext): List { - val middleware = super.getDefaultHttpMiddleware(ctx) - - val queryMiddleware = listOf( - // ensure content-type gets set - // see: https://awslabs.github.io/smithy/1.0/spec/aws/aws-query-protocol.html#protocol-behavior - MutateHeadersMiddleware(addMissingHeaders = mapOf("Content-Type" to QueryContentType)), - ) - - return middleware + queryMiddleware - } - - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver = - QueryBindingResolver(model, serviceShape) - - override fun renderSerializeHttpBody( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - val input = ctx.model.expectShape(op.input.get()) - if (input.members().isEmpty()) { - // if there is no payload serialized we still need to add the literals that define the operation being - // invoked - // see: https://awslabs.github.io/smithy/1.0/spec/aws/aws-query-protocol.html#request-serialization - val action = op.id.name - val service = ctx.model.expectShape(ctx.settings.service) - val version = service.version - writer.write("""val content = "Action=$action&Version=$version"""") - writer.write("builder.body = #T.fromBytes(content.encodeToByteArray())", RuntimeTypes.Http.HttpBody) - } else { - super.renderSerializeHttpBody(ctx, op, writer) - } - } -} - -/** - * An HTTP binding resolver for the query binding protocols - */ -class QueryBindingResolver( - model: Model, - service: ServiceShape, -) : StaticHttpBindingResolver(model, service, QueryHttpTrait, QueryContentType, TimestampFormatTrait.Format.DATE_TIME) { - constructor(ctx: ProtocolGenerator.GenerationContext) : this(ctx.model, ctx.service) - - companion object { - val QueryHttpTrait: HttpTrait = HttpTrait - .builder() - .code(200) - .method("POST") - .uri(UriPattern.parse("/")) - .build() - } -} - -abstract class AbstractQueryFormUrlSerializerGenerator( - private val protocolGenerator: ProtocolGenerator, - private val defaultTimestampFormat: TimestampFormatTrait.Format, -) : StructuredDataSerializerGenerator { - - abstract fun descriptorGenerator( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ): FormUrlSerdeDescriptorGenerator - - override fun operationSerializer(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, members: List): Symbol { - val input = op.input.get().let { ctx.model.expectShape(it) } - val symbol = ctx.symbolProvider.toSymbol(input) - - return op.bodySerializer(ctx.settings) { writer -> - addNestedDocumentSerializers(ctx, op, writer) - val fnName = op.bodySerializerName() - writer.openBlock("private fun #L(context: #T, input: #T): ByteArray {", fnName, RuntimeTypes.Core.ExecutionContext, symbol) - .call { - renderSerializeOperationBody(ctx, op, members, writer) - } - .closeBlock("}") - } - } - - /** - * Register nested structure/map shapes reachable from the operation input shape that require a "document" serializer - * implementation - */ - private fun addNestedDocumentSerializers(ctx: ProtocolGenerator.GenerationContext, shape: Shape, writer: KotlinWriter) { - val serdeIndex = SerdeIndex.of(ctx.model) - val shapesRequiringDocumentSerializer = serdeIndex.requiresDocumentSerializer(shape) - // register a dependency on each of the members that require a serializer impl - // ensuring they get generated - shapesRequiringDocumentSerializer.forEach { - val nestedStructOrUnionSerializer = documentSerializer(ctx, it) - writer.addImport(nestedStructOrUnionSerializer) - } - } - - private fun renderSerializeOperationBody( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - documentMembers: List, - writer: KotlinWriter, - ) { - val shape = ctx.model.expectShape(op.input.get()) - writer.write("val serializer = #T()", RuntimeTypes.Serde.SerdeFormUrl.FormUrlSerializer) - renderSerializerBody(ctx, shape, documentMembers, writer) - writer.write("return serializer.toByteArray()") - } - - private fun documentSerializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection = shape.members(), - ): Symbol { - val symbol = ctx.symbolProvider.toSymbol(shape) - 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) - } - .closeBlock("}") - } - } - - private fun renderSerializerBody( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ) { - // render the serde descriptors - descriptorGenerator(ctx, shape, members, writer).render() - when (shape) { - is UnionShape -> SerializeUnionGenerator(ctx, shape, members, writer, defaultTimestampFormat).render() - else -> SerializeStructGenerator(ctx, members, writer, defaultTimestampFormat).render() - } - } - - override fun payloadSerializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection?, - ): Symbol { - // re-use document serializer (for the target shape!) - val target = shape.targetOrSelf(ctx.model) - val symbol = ctx.symbolProvider.toSymbol(shape) - 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 #identifier.name:L(input: #T): ByteArray {", "}", symbol) { - write("val serializer = #T()", RuntimeTypes.Serde.SerdeFormUrl.FormUrlSerializer) - write("#T(serializer, input)", serializeFn) - write("return serializer.toByteArray()") - } - } - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/StaticHttpBindingResolver.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/StaticHttpBindingResolver.kt deleted file mode 100644 index f549d0ed4c7..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/StaticHttpBindingResolver.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.core - -import software.amazon.smithy.kotlin.codegen.model.expectTrait -import software.amazon.smithy.kotlin.codegen.model.hasTrait -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingDescriptor -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.knowledge.TopDownIndex -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.* - -/** - * An HTTP binding resolver that uses a (synthetic) static [HttpTrait] - * - * NOTE: The [HttpTrait] is not attached or otherwise associated with the models (otherwise use [HttpTraitResolver]). - */ -open class StaticHttpBindingResolver( - protected val model: Model, - protected val service: ServiceShape, - protected val httpTrait: HttpTrait, - protected val defaultContentType: String, - protected val defaultTimestampFormat: TimestampFormatTrait.Format, -) : HttpBindingResolver { - constructor( - context: ProtocolGenerator.GenerationContext, - httpTrait: HttpTrait, - defaultContentType: String, - defaultTimestampFormat: TimestampFormatTrait.Format, - ) : this(context.model, context.service, httpTrait, defaultContentType, defaultTimestampFormat) - - protected val topDownIndex: TopDownIndex = TopDownIndex.of(model) - - override fun determineRequestContentType(operationShape: OperationShape): String = defaultContentType - - override fun httpTrait(operationShape: OperationShape): HttpTrait = httpTrait - - override fun determineTimestampFormat( - member: ToShapeId, - location: HttpBinding.Location, - defaultFormat: TimestampFormatTrait.Format, - ): TimestampFormatTrait.Format = defaultTimestampFormat - - /** - * All operations are binding for the model. - */ - override fun bindingOperations(): List = - topDownIndex.getContainedOperations(service).toList() - - /** - * By default returns all inputs as [HttpBinding.Location.DOCUMENT] - */ - override fun requestBindings(operationShape: OperationShape): List { - if (!operationShape.input.isPresent) return emptyList() - val input = model.expectShape(operationShape.input.get()) - return input.members().map { member -> HttpBindingDescriptor(member, HttpBinding.Location.DOCUMENT) }.toList() - } - - /** - * By default returns all outputs as [HttpBinding.Location.DOCUMENT] - */ - override fun responseBindings(shape: Shape): List = when (shape) { - is OperationShape -> - shape - .output - .map { model.expectShape(it).members() } - .orElseGet { emptyList() } - .map { member -> HttpBindingDescriptor(member, HttpBinding.Location.DOCUMENT) } - is StructureShape -> shape.members().map { member -> member.toHttpBindingDescriptor() }.toList() - else -> error("unimplemented shape type for http response bindings: $shape") - } -} - -// Create a [HttpBindingDescriptor] based on traits on [MemberShape] -// See https://awslabs.github.io/smithy/1.0/spec/core/http-traits.html -private fun MemberShape.toHttpBindingDescriptor(): HttpBindingDescriptor = - when { - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.HEADER, expectTrait().value) - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.LABEL) - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.PAYLOAD) - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.QUERY, expectTrait().value) - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.RESPONSE_CODE) - hasTrait() -> HttpBindingDescriptor(this, HttpBinding.Location.PREFIX_HEADERS, expectTrait().value) - // By default, all structure members that are not bound as part of the HTTP message are - // serialized in a protocol-specific document sent in the body of the message - else -> HttpBindingDescriptor(this, HttpBinding.Location.DOCUMENT) - // NOTE: Unsure of where (if anywhere) HttpBinding.Location.UNBOUND should be modeled - } diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamParserGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamParserGenerator.kt deleted file mode 100644 index 92845b590a8..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamParserGenerator.kt +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.eventstream - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.core.* -import software.amazon.smithy.kotlin.codegen.model.* -import software.amazon.smithy.kotlin.codegen.rendering.ExceptionBaseClassGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.StructuredDataParserGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.bodyDeserializer -import software.amazon.smithy.kotlin.codegen.rendering.serde.bodyDeserializerName -import software.amazon.smithy.kotlin.codegen.utils.getOrNull -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.EventHeaderTrait -import software.amazon.smithy.model.traits.EventPayloadTrait -import software.amazon.smithy.model.traits.StreamingTrait - -/** - * A set of RPC-bound Smithy protocols - */ -val RPC_BOUND_PROTOCOLS = setOf( - "awsJson1_0", - "awsJson1_1", - "awsQuery", - "ec2Query", -) - -/** - * Represents whether the given ShapeId represents an RPC-bound Smithy protocol - */ -internal val ShapeId.isRpcBoundProtocol: Boolean - get() = RPC_BOUND_PROTOCOLS.contains(name) - -/** - * Implements rendering deserialize implementation for event streams implemented using the - * `vnd.amazon.event-stream` content-type - * - * @param sdg the structured data parser generator - */ -class EventStreamParserGenerator( - private val ctx: ProtocolGenerator.GenerationContext, - private val sdg: StructuredDataParserGenerator, -) { - /** - * Return the function responsible for deserializing an operation output that targets an event stream - * - * ``` - * private suspend fun deserializeFooOperationBody(builder: Foo.Builder, body: HttpBody) { ... } - * ``` - */ - fun responseHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol = - op.bodyDeserializer(ctx.settings) { writer -> - val outputSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(op.output.get())) - // we have access to the builder for the output type and the full HttpBody - // members bound via HTTP bindings (e.g. httpHeader, statusCode, etc) are already deserialized via HttpDeserialize impl - // we just need to deserialize the event stream member (and/or the initial response) - writer.withBlock( - // FIXME - revert to private, exposed as internal temporarily while we figure out integration tests - "internal suspend fun #L(builder: #T.Builder, call: #T) {", - "}", - op.bodyDeserializerName(), - outputSymbol, - RuntimeTypes.Http.HttpCall, - ) { - renderDeserializeEventStream(ctx, op, writer) - } - } - - private fun renderDeserializeEventStream(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { - val output = ctx.model.expectShape(op.output.get()) - val streamingMember = output.findStreamingMember(ctx.model) ?: error("expected a streaming member for $output") - val streamShape = ctx.model.expectShape(streamingMember.target) - val streamSymbol = ctx.symbolProvider.toSymbol(streamShape) - - val messageTypeSymbol = RuntimeTypes.AwsEventStream.MessageType - val baseExceptionSymbol = ExceptionBaseClassGenerator.baseExceptionSymbol(ctx.settings) - - writer.write("val chan = call.response.body.#T(call) ?: return", RuntimeTypes.Http.toSdkByteReadChannel) - writer.write("val frames = #T(chan)", RuntimeTypes.AwsEventStream.decodeFrames) - if (ctx.protocol.isRpcBoundProtocol) { - renderDeserializeInitialResponse(ctx, output, writer) - } else { - writer.write("val events = frames") - } - writer.indent() - .withBlock(".#T { message ->", "}", RuntimeTypes.KotlinxCoroutines.Flow.map) { - withBlock("when (val mt = message.#T()) {", "}", RuntimeTypes.AwsEventStream.MessageTypeExt) { - withBlock("is #T.Event -> when (mt.shapeType) {", "}", messageTypeSymbol) { - streamShape.filterEventStreamErrors(ctx.model).forEach { member -> - withBlock("#S -> {", "}", member.memberName) { - renderDeserializeEventVariant(ctx, streamSymbol, member, writer) - } - } - - write("else -> #T.SdkUnknown", streamSymbol) - } - withBlock("is #T.Exception -> when (mt.shapeType) {", "}", messageTypeSymbol) { - // errors are completely bound to payload (at least according to design docs) - val errorMembers = streamShape.members().filter { - val target = ctx.model.expectShape(it.target) - target.isError - } - errorMembers.forEach { member -> - withBlock("#S -> {", "}", member.memberName) { - val payloadDeserializeFn = sdg.payloadDeserializer(ctx, member) - write("val err = #T(message.payload)", payloadDeserializeFn) - write("throw err") - } - } - write("else -> throw #T(#S)", baseExceptionSymbol, "error processing event stream, unrecognized errorType: \${mt.shapeType}") - } - // this is a service exception still, just un-modeled - write("is #T.Error -> throw #T(\"error processing event stream: errorCode=\${mt.errorCode}; message=\${mt.message}\")", messageTypeSymbol, baseExceptionSymbol) - // this is a client exception because we failed to parse it - write("is #T.SdkUnknown -> throw #T(\"unrecognized event stream message `:message-type`: \${mt.messageType}\")", messageTypeSymbol, RuntimeTypes.Core.ClientException) - } - } - .dedent() - .write("") - - writer.write("builder.#L = events", streamingMember.defaultName()) - } - - private fun renderDeserializeEventVariant(ctx: ProtocolGenerator.GenerationContext, unionSymbol: Symbol, member: MemberShape, writer: KotlinWriter) { - val variant = ctx.model.expectShape(member.target) - - val eventHeaderBindings = variant.members().filter { it.hasTrait() } - val eventPayloadBinding = variant.members().firstOrNull { it.hasTrait() } - val unbound = variant.members().filterNot { it.hasTrait() || it.hasTrait() } - - 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 eb = #T.Builder()", variantSymbol) - - // render members bound to header - eventHeaderBindings.forEach { hdrBinding -> - val target = ctx.model.expectShape(hdrBinding.target) - val targetSymbol = ctx.symbolProvider.toSymbol(target) - - // :test(boolean, byte, short, integer, long, blob, string, timestamp)) - val conversionFn = when (target.type) { - ShapeType.BOOLEAN -> RuntimeTypes.AwsEventStream.expectBool - ShapeType.BYTE -> RuntimeTypes.AwsEventStream.expectByte - ShapeType.SHORT -> RuntimeTypes.AwsEventStream.expectInt16 - ShapeType.INTEGER -> RuntimeTypes.AwsEventStream.expectInt32 - ShapeType.LONG -> RuntimeTypes.AwsEventStream.expectInt64 - ShapeType.BLOB -> RuntimeTypes.AwsEventStream.expectByteArray - ShapeType.STRING -> RuntimeTypes.AwsEventStream.expectString - ShapeType.TIMESTAMP -> RuntimeTypes.AwsEventStream.expectTimestamp - else -> throw CodegenException("unsupported eventHeader shape: member=$hdrBinding; targetShape=$target") - } - - val defaultValuePostfix = if (targetSymbol.isNotNullable && targetSymbol.defaultValue() != null) { - " ?: ${targetSymbol.defaultValue()}" - } else { - "" - } - 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 { - if (unbound.isNotEmpty()) { - // all remaining members are bound to payload (but not explicitly bound via @eventPayload) - // generate a payload deserializer specific to the unbound members (note this will be a deserializer - // for the overall event shape but only payload members will be considered for deserialization), - // and then assign each deserialized payload member to the current builder instance - val payloadDeserializeFn = sdg.payloadDeserializer(ctx, member, unbound) - writer.write("val tmp = #T(message.payload)", payloadDeserializeFn) - unbound.forEach { - writer.write("eb.#1L = tmp.#1L", it.defaultName()) - } - } - } - - writer.write("val e = eb.build()") - } - - writer.write("#T.#L(e)", unionSymbol, member.unionVariantName()) - } - - /** - * Renders deserialization logic for a message with the `initial-response` type. - */ - private fun renderDeserializeInitialResponse(ctx: ProtocolGenerator.GenerationContext, outputShape: StructureShape, writer: KotlinWriter) { - // A custom function which only deserializes the initial response members - val initialResponseDeserializeFn = sdg.payloadDeserializer(ctx, outputShape, outputShape.initialResponseMembers) - - writer.write( - "val firstMessage = frames.#T(1).#T()", - RuntimeTypes.KotlinxCoroutines.Flow.take, - RuntimeTypes.KotlinxCoroutines.Flow.single, - ) - writer.write("val firstMessageType = firstMessage.type()") - writer.openBlock( - "val events = if (firstMessageType is #T.Event && firstMessageType.shapeType == \"initial-response\") {", - RuntimeTypes.AwsEventStream.MessageType, - ) - // Deserialize into `initialResponse`, then apply it to the actual response builder - writer.write("val initialResponse = #T(firstMessage.payload)", initialResponseDeserializeFn) - writer.withBlock("builder.apply {", "}") { - outputShape.initialResponseMembers.forEach { member -> - writer.write("#1L = initialResponse.#1L", member.defaultName()) - } - } - .write("frames") - .closeAndOpenBlock("} else {") - .write( - "#T(#T(firstMessage), frames)", - RuntimeTypes.Core.Utils.mergeSequential, - RuntimeTypes.KotlinxCoroutines.Flow.flowOf, - ) - .closeBlock("}") - } - - /** - * Get all the shape's members which aren't an event stream - */ - private val StructureShape.initialResponseMembers get() = members().filter { - val targetShape = ctx.model.getShape(it.target).getOrNull() - targetShape?.hasTrait() == false - } - - private fun renderDeserializeExplicitEventPayloadMember( - ctx: ProtocolGenerator.GenerationContext, - member: MemberShape, - writer: KotlinWriter, - ) { - // 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("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("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") - } - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamSerializerGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamSerializerGenerator.kt deleted file mode 100644 index 6fb3c803fa3..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/eventstream/EventStreamSerializerGenerator.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.eventstream - -import software.amazon.smithy.codegen.core.CodegenException -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.core.* -import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes -import software.amazon.smithy.kotlin.codegen.model.* -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.kotlin.codegen.utils.getOrNull -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.EventHeaderTrait -import software.amazon.smithy.model.traits.EventPayloadTrait -import software.amazon.smithy.model.traits.StreamingTrait - -/** - * Implements rendering serialize implementation for event streams implemented using the - * `vnd.amazon.event-stream` content-type - * - * @param sdg the structured data serializer generator - * @param payloadContentType the content-type to use when sending structured data (e.g. `application/json`) - */ -class EventStreamSerializerGenerator( - private val sdg: StructuredDataSerializerGenerator, - private val payloadContentType: String, -) { - - /** - * Return the function responsible for serializing an operation output that targets an event stream - * - * ``` - * private suspend fun serializeFooOperationBody(input: FooInput): HttpBody { ... } - * ``` - */ - fun requestHandler(ctx: ProtocolGenerator.GenerationContext, op: OperationShape): Symbol = - op.bodySerializer(ctx.settings) { writer -> - val inputSymbol = ctx.symbolProvider.toSymbol(ctx.model.expectShape(op.input.get())) - writer.withBlock( - // FIXME - revert to private, exposed as internal temporarily while we figure out integration tests - "internal suspend fun #L(context: #T, input: #T): #T {", - "}", - op.bodySerializerName(), - RuntimeTypes.Core.ExecutionContext, - inputSymbol, - RuntimeTypes.Http.HttpBody, - ) { - renderSerializeEventStream(ctx, op, writer) - } - } - - private fun renderSerializeEventStream( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - val input = ctx.model.expectShape(op.input.get()) - val streamingMember = input.findStreamingMember(ctx.model) ?: error("expected a streaming member for $input") - val streamShape = ctx.model.expectShape(streamingMember.target) - - writer.write("val stream = input.#L ?: return #T.Empty", streamingMember.defaultName(), RuntimeTypes.Http.HttpBody) - - // initial HTTP request should use an empty body hash since the actual body is the event stream - writer.write("context[#T.HashSpecification] = #T.EmptyBody", RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes, RuntimeTypes.Auth.Signing.AwsSigningCommon.HashSpecification) - - // FIXME - we need a signer implementation which usually comes from the auth scheme...for now default to default signer for event streams - writer.write("context[#T.Signer] = #T", RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes, RuntimeTypes.Auth.Signing.AwsSigningStandard.DefaultAwsSigner) - // ensure a deferred is set, signer will complete it when initial request signature is available - writer.write( - "context[#T.RequestSignature] = #T(context.coroutineContext.#T)", - RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes, - RuntimeTypes.KotlinxCoroutines.CompletableDeferred, - RuntimeTypes.KotlinxCoroutines.job, - ) - - val encodeFn = encodeEventStreamMessage(ctx, op, streamShape) - - writer.write("") - val initialRequestMembers = input.initialRequestMembers(ctx) - if (ctx.protocol.isRpcBoundProtocol && initialRequestMembers.isNotEmpty()) { - val serializerFn = sdg.payloadSerializer(ctx, input, initialRequestMembers) - - writer.withBlock("val initialRequest = buildMessage {", "}") { - writer.write("addHeader(\":message-type\", HeaderValue.String(\"event\"))") - writer.write("addHeader(\":event-type\", HeaderValue.String(\"initial-request\"))") - writer.write("payload = #T(input)", serializerFn) - } - - writer.withBlock( - "val messages = #T(#T(initialRequest), stream.#T(::#T))", - "", - RuntimeTypes.Core.Utils.mergeSequential, - RuntimeTypes.KotlinxCoroutines.Flow.flowOf, - RuntimeTypes.KotlinxCoroutines.Flow.map, - encodeFn, - ) { - write(".#T(context)", RuntimeTypes.AwsEventStream.sign) - write(".#T()", RuntimeTypes.AwsEventStream.encode) - } - } else { - writer.withBlock("val messages = stream", "") { - write(".#T(::#T)", RuntimeTypes.KotlinxCoroutines.Flow.map, encodeFn) - write(".#T(context)", RuntimeTypes.AwsEventStream.sign) - write(".#T()", RuntimeTypes.AwsEventStream.encode) - } - } - - writer.write("") - writer.write("return messages.#T(context)", RuntimeTypes.AwsEventStream.asEventStreamHttpBody) - } - - private fun encodeEventStreamMessage( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - streamShape: UnionShape, - ): Symbol = buildSymbol { - val streamSymbol = ctx.symbolProvider.toSymbol(streamShape) - val fnName = "encode${op.capitalizedDefaultName()}${streamSymbol.name}EventMessage" - name = fnName - namespace = ctx.settings.pkg.serde - // place it in same file as the operation serializer - definitionFile = "${op.serializerName()}.kt" - - renderBy = { writer -> - // TODO - make internal and share across operations? - writer.withBlock( - "private fun #L(input: #T): #T = #T {", - "}", - fnName, - streamSymbol, - RuntimeTypes.AwsEventStream.Message, - RuntimeTypes.AwsEventStream.buildMessage, - ) { - addStringHeader(":message-type", "event") - - withBlock("when(input) {", "}") { - streamShape.filterEventStreamErrors(ctx.model) - .forEach { member -> - withBlock( - "is #T.#L -> {", - "}", - streamSymbol, - member.unionVariantName(), - ) { - addStringHeader(":event-type", member.memberName) - val variant = ctx.model.expectShape(member.target) - - val eventHeaderBindings = variant.members().filter { it.hasTrait() } - val eventPayloadBinding = variant.members().firstOrNull { it.hasTrait() } - val unbound = variant.members().filterNot { it.hasTrait() || it.hasTrait() } - - 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) - } - } - } - } - write("is #T.SdkUnknown -> error(#S)", streamSymbol, "cannot serialize the unknown event type!") - } - } - } - } - - private fun renderSerializeEventHeader(ctx: ProtocolGenerator.GenerationContext, member: MemberShape, writer: KotlinWriter) { - val target = ctx.model.expectShape(member.target) - val headerValue = when (target.type) { - ShapeType.BOOLEAN -> "Bool" - ShapeType.BYTE -> "Byte" - ShapeType.SHORT -> "Int16" - ShapeType.INTEGER -> "Int32" - ShapeType.LONG -> "Int64" - ShapeType.BLOB -> "ByteArray" - ShapeType.STRING -> "String" - ShapeType.TIMESTAMP -> "Timestamp" - else -> throw CodegenException("unsupported shape type `${target.type}` for eventHeader member `$member`; target: $target") - } - val conversion = if (target.type == ShapeType.BYTE) ".toUByte()" else "" - - writer.write( - "input.value.#L?.let { addHeader(#S, #T.#L(it$conversion)) }", - member.defaultName(), - member.memberName, - RuntimeTypes.AwsEventStream.HeaderValue, - headerValue, - ) - } - - private fun renderSerializeEventPayload(ctx: ProtocolGenerator.GenerationContext, member: MemberShape, writer: KotlinWriter) { - // structure > :test(member > :test(blob, string, structure, union)) - val target = ctx.model.expectShape(member.target) - when (target.type) { - // input is the sealed class, each variant is generated with `value` as the property name of the type being wrapped - ShapeType.BLOB -> { - writer.addStringHeader(":content-type", "application/octet-stream") - writer.write("payload = input.value.#L", member.defaultName()) - } - ShapeType.STRING -> { - writer.addStringHeader(":content-type", "text/plain") - writer.write("payload = input.value.#L?.#T()", member.defaultName(), KotlinTypes.Text.encodeToByteArray) - } - ShapeType.STRUCTURE, ShapeType.UNION -> { - writer.addStringHeader(":content-type", payloadContentType) - // re-use the payload serializer - val serializeFn = sdg.payloadSerializer(ctx, member) - writer.write("payload = input.value.#L?.let { #T(it) }", member.defaultName(), serializeFn) - } - else -> throw CodegenException("unsupported shape type `${target.type}` for target: $target; expected blob, string, structure, or union for eventPayload member: $member") - } - } - - private fun KotlinWriter.addStringHeader(name: String, value: String) { - write("addHeader(#S, #T.String(#S))", name, RuntimeTypes.AwsEventStream.HeaderValue, value) - } - - /** - * Get all the shape's members which aren't an event stream - */ - private fun StructureShape.initialRequestMembers(ctx: ProtocolGenerator.GenerationContext) = members().filter { - val targetShape = ctx.model.getShape(it.target).getOrNull() - targetShape?.hasTrait() == false - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/formurl/QuerySerdeFormUrlDescriptorGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/formurl/QuerySerdeFormUrlDescriptorGenerator.kt deleted file mode 100644 index 994b242aa49..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/formurl/QuerySerdeFormUrlDescriptorGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.protocols.formurl - -import software.amazon.smithy.kotlin.codegen.core.RenderingContext -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.model.changeNameSuffix -import software.amazon.smithy.kotlin.codegen.model.hasTrait -import software.amazon.smithy.kotlin.codegen.model.traits.OperationInput -import software.amazon.smithy.kotlin.codegen.rendering.serde.FormUrlSerdeDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.SdkFieldDescriptorTrait -import software.amazon.smithy.kotlin.codegen.rendering.serde.add -import software.amazon.smithy.kotlin.codegen.utils.dq -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape - -/** - * A generalized superclass for descriptor generators that follow the "*query" AWS protocols. - */ -abstract class QuerySerdeFormUrlDescriptorGenerator( - ctx: RenderingContext, - memberShapes: List? = null, -) : FormUrlSerdeDescriptorGenerator(ctx, memberShapes) { - override fun getObjectDescriptorTraits(): List { - val traits = super.getObjectDescriptorTraits().toMutableList() - - val objectShape = requireNotNull(ctx.shape) - if (objectShape.hasTrait()) { - // see https://awslabs.github.io/smithy/1.0/spec/aws/aws-query-protocol.html#request-serialization - - // operation inputs are normalized in smithy-kotlin::OperationNormalizer to be "[OperationName]Request" - val action = objectShape.changeNameSuffix("Request" to "") - val version = service.version - traits.add(RuntimeTypes.Serde.SerdeFormUrl.QueryLiteral, "Action".dq(), action.dq()) - traits.add(RuntimeTypes.Serde.SerdeFormUrl.QueryLiteral, "Version".dq(), version.dq()) - } - - return traits - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolver.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolver.kt deleted file mode 100644 index eba5e590788..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolver.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.StaticHttpBindingResolver -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.pattern.UriPattern -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.traits.* - -/** - * An HTTP binding resolver for the awsJson protocol(s). - */ -class AwsJsonHttpBindingResolver( - model: Model, - serviceShape: ServiceShape, - defaultContentType: String, -) : StaticHttpBindingResolver(model, serviceShape, AwsJsonHttpTrait, defaultContentType, TimestampFormatTrait.Format.EPOCH_SECONDS) { - companion object { - val AwsJsonHttpTrait: HttpTrait = HttpTrait - .builder() - .code(200) - .method("POST") - .uri(UriPattern.parse("/")) - .build() - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolMiddleware.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolMiddleware.kt deleted file mode 100644 index e9ecfb37db9..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolMiddleware.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId - -/** - * Configure the AwsJsonProtocol middleware - * @param protocolVersion The AWS JSON protocol version (e.g. "1.0", "1.1", etc) - */ -class AwsJsonProtocolMiddleware( - private val serviceShapeId: ShapeId, - private val protocolVersion: String, -) : ProtocolMiddleware { - override val name: String = "AwsJsonProtocol" - override val order: Byte = 10 - - override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { - writer.write("op.install(#T(#S, #S))", RuntimeTypes.AwsJsonProtocols.AwsJsonProtocol, serviceShapeId.name, protocolVersion) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolParserGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolParserGenerator.kt deleted file mode 100644 index e98d29acf1e..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolParserGenerator.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.toRenderingContext -import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonParserGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonSerdeDescriptorGenerator -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape - -/** - * Overrides the [JsonParserGenerator] when using `AWS Json 1.0`, `AWS Json 1.1`, and `AWS RestJson 1` protocols. - * - * See https://github.com/smithy-lang/smithy/pull/1945 - */ -class AwsJsonProtocolParserGenerator( - private val protocolGenerator: ProtocolGenerator, - private val supportsJsonNameTrait: Boolean = true, -) : JsonParserGenerator(protocolGenerator, supportsJsonNameTrait) { - - override fun descriptorGenerator( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: List, - writer: KotlinWriter, - ): JsonSerdeDescriptorGenerator = AwsJsonProtocolSerdeDescriptorGenerator(ctx.toRenderingContext(protocolGenerator, shape, writer), members, supportsJsonNameTrait) -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGenerator.kt deleted file mode 100644 index a6471e992f2..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGenerator.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.core.RenderingContext -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.rendering.serde.JsonSerdeDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.SdkFieldDescriptorTrait -import software.amazon.smithy.kotlin.codegen.rendering.serde.add -import software.amazon.smithy.kotlin.codegen.utils.dq -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape - -/** - * Overrides the [JsonSerdeDescriptorGenerator] when using `AWS Json 1.0`, `AWS Json 1.1`, and `AWS RestJson 1` protocols. - * - * See: https://github.com/smithy-lang/smithy/pull/1945 - */ -class AwsJsonProtocolSerdeDescriptorGenerator( - ctx: RenderingContext, - memberShapes: List? = null, - supportsJsonNameTrait: Boolean = true, -) : JsonSerdeDescriptorGenerator(ctx, memberShapes, supportsJsonNameTrait) { - - /** - * Adds a trait to ignore `__type` in union shapes for `AWS Json 1.0`, `AWS Json 1.1`, `RestJson 1.0` protocols - * Sometimes the unnecessary field `__type` is added and needs to be ignored - * - * NOTE: Will be ignored unless it's in the model - * - * Source: https://github.com/smithy-lang/smithy/pull/1945 - */ - override fun getObjectDescriptorTraits(): List { - val traitList = super.getObjectDescriptorTraits().toMutableList() - val typeMember = memberShapes.find { it.memberName == "__type" } - - if (ctx.shape?.isUnionShape == true && typeMember == null) { - traitList.add(RuntimeTypes.Serde.SerdeJson.IgnoreKey, "__type".dq()) - } - - return traitList - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/JsonHttpBindingProtocolGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/JsonHttpBindingProtocolGenerator.kt deleted file mode 100644 index 7e82faae3d8..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/JsonHttpBindingProtocolGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.aws.protocols.core.AwsHttpBindingProtocolGenerator -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.TimestampFormatTrait - -/** - * Abstract base class that all protocols using JSON as a document format can inherit from - */ -abstract class JsonHttpBindingProtocolGenerator : AwsHttpBindingProtocolGenerator() { - - override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.EPOCH_SECONDS - - /** - * Flag indicating if the jsonName trait is supported or not. When true the trait is processed when generating - * serializers and deserializers. When false the member name is used. - */ - open val supportsJsonNameTrait: Boolean = true - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - JsonParserGenerator(this, supportsJsonNameTrait = supportsJsonNameTrait) - - override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = - JsonSerializerGenerator(this, supportsJsonNameTrait = supportsJsonNameTrait) - - override fun renderDeserializeErrorDetails( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - writer.write("#T.deserialize(call.response.headers, payload)", RuntimeTypes.AwsJsonProtocols.RestJsonErrorDeserializer) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGenerator.kt b/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGenerator.kt deleted file mode 100644 index e71d2db2765..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGenerator.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.xml - -import software.amazon.smithy.kotlin.codegen.core.RenderingContext -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.model.isError -import software.amazon.smithy.kotlin.codegen.rendering.serde.SdkFieldDescriptorTrait -import software.amazon.smithy.kotlin.codegen.rendering.serde.XmlSerdeDescriptorGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.add -import software.amazon.smithy.kotlin.codegen.utils.dq -import software.amazon.smithy.kotlin.codegen.utils.toggleFirstCharacterCase -import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape - -/** - * restXml-specific descriptor generator - */ -class RestXmlSerdeDescriptorGenerator( - ctx: RenderingContext, - memberShapes: List? = null, -) : XmlSerdeDescriptorGenerator(ctx, memberShapes) { - override fun getFieldDescriptorTraits( - member: MemberShape, - targetShape: Shape, - nameSuffix: String, - ): List { - val traitList = super.getFieldDescriptorTraits(member, targetShape, nameSuffix).toMutableList() - - if (ctx.shape?.isError == true) { - val serialName = getSerialName(member, nameSuffix) - if (serialName.equals("message", ignoreCase = true)) { - // Need to be able to read error messages from "Message" or "message" - // https://github.com/awslabs/smithy-kotlin/issues/352 - traitList.add(RuntimeTypes.Serde.SerdeXml.XmlAliasName, serialName.toggleFirstCharacterCase().dq()) - } - } - - return traitList - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration b/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration deleted file mode 100644 index 07cdaabee7e..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +++ /dev/null @@ -1,3 +0,0 @@ -software.amazon.smithy.kotlin.codegen.aws.SdkProtocolGeneratorSupplier -software.amazon.smithy.kotlin.codegen.aws.customization.RegionSupport -software.amazon.smithy.kotlin.codegen.aws.middleware.ClockSkew diff --git a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGeneratorTest.kt b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGeneratorTest.kt deleted file mode 100644 index 19a19638442..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/core/AwsHttpBindingProtocolGeneratorTest.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package software.amazon.smithy.kotlin.codegen.aws.protocols.core - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.model.expectShape -import software.amazon.smithy.kotlin.codegen.rendering.protocol.HttpBindingResolver -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.serde.* -import software.amazon.smithy.kotlin.codegen.test.defaultSettings -import software.amazon.smithy.kotlin.codegen.test.newTestContext -import software.amazon.smithy.kotlin.codegen.test.shouldContainOnlyOnceWithDiff -import software.amazon.smithy.kotlin.codegen.test.toSmithyModel -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.* -import software.amazon.smithy.model.traits.TimestampFormatTrait -import kotlin.test.Test - -class AwsHttpBindingProtocolGeneratorTest { - - @Test - fun itThrowsBaseServiceExceptionOnErrorParseFailure() { - val model = """ - namespace com.test - use aws.protocols#restJson1 - - @restJson1 - service Example { - version: "1.0.0", - operations: [GetFoo] - } - - operation GetFoo { - errors: [FooError] - } - - @error("server") - structure FooError { - payload: String - } - """.toSmithyModel() - - // This is the value that produces the name of the service base exception type - val serviceSdkName = "SdkName" - - val testCtx = model.newTestContext( - serviceName = "Example", - settings = model.defaultSettings(sdkId = serviceSdkName), - ) - val unit = TestableAwsHttpBindingProtocolGenerator() - val op = model.expectShape("com.test#GetFoo") - - val fn = unit.operationErrorHandler(testCtx.generationCtx, op) - - // use the symbol to ensure it's generated via GeneratedDependency - testCtx.generationCtx.delegator.useFileWriter("GetFooOperationDeserializer.kt", "com.test.serde") { - it.write("#T(context, response)", fn) - } - - testCtx.generationCtx.delegator.finalize() - testCtx.generationCtx.delegator.flushWriters() - val actual = testCtx.manifest.expectFileString("src/main/kotlin/com/test/serde/GetFooOperationDeserializer.kt") - val expected = """ - throw ${serviceSdkName}Exception("Failed to parse response as 'restJson1' error", ex).also { - """.trimIndent() - - actual.shouldContainOnlyOnceWithDiff(expected) - } - - // A concrete implementation of AwsHttpBindingProtocolGenerator to exercise renderThrowOperationError() - class TestableAwsHttpBindingProtocolGenerator : AwsHttpBindingProtocolGenerator() { - override fun renderDeserializeErrorDetails( - ctx: ProtocolGenerator.GenerationContext, - op: OperationShape, - writer: KotlinWriter, - ) { - // NOP - } - - override val defaultTimestampFormat: TimestampFormatTrait.Format - get() = error("Unneeded for test") - - override fun getProtocolHttpBindingResolver(model: Model, serviceShape: ServiceShape): HttpBindingResolver { - error("Unneeded for test") - } - - override fun structuredDataParser(ctx: ProtocolGenerator.GenerationContext): StructuredDataParserGenerator = - object : StructuredDataParserGenerator { - override fun operationDeserializer(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, members: List): Symbol { - error("Unneeded for test") - } - - override fun errorDeserializer( - ctx: ProtocolGenerator.GenerationContext, - errorShape: StructureShape, - members: List, - ): Symbol { - error("Unneeded for test") - } - - override fun payloadDeserializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection?, - ): Symbol { - error("Unneeded for test") - } - } - - override fun structuredDataSerializer(ctx: ProtocolGenerator.GenerationContext): StructuredDataSerializerGenerator = - object : StructuredDataSerializerGenerator { - override fun operationSerializer(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, members: List): Symbol { - error("Unneeded for test") - } - - override fun payloadSerializer( - ctx: ProtocolGenerator.GenerationContext, - shape: Shape, - members: Collection?, - ): Symbol { - error("Unneeded for test") - } - } - - override val protocol: ShapeId - get() = error("Unneeded for test") - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolverTest.kt b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolverTest.kt deleted file mode 100644 index 56036239744..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonHttpBindingResolverTest.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.model.expectShape -import software.amazon.smithy.kotlin.codegen.test.newTestContext -import software.amazon.smithy.kotlin.codegen.test.toSmithyModel -import software.amazon.smithy.model.knowledge.HttpBinding -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class AwsJsonHttpBindingResolverTest { - private val testModel = """ - namespace smithy.example - - use aws.protocols#awsJson1_0 - - @awsJson1_0 - service Example { - version: "1.0.0", - operations: [GetEmptyFoo, GetFoo] - } - - operation GetEmptyFoo { } - - operation GetFoo { - input: GetFooInput, - output: GetFooOutput, - errors: [GetFooError] - } - - structure GetFooInput { - bigInt: BigInteger - } - structure GetFooOutput { - bigInt: BigInteger - } - - @error("client") - structure GetFooError {} - """.toSmithyModel(applyDefaultTransforms = false) - - @Test - fun `it resolves all operations associated with a service`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val expectedOperations = listOf("GetEmptyFoo", "GetFoo") - val actualOperations = unit.bindingOperations().map { operationShape -> operationShape.id.name }.sorted() - - assertEquals(expectedOperations, actualOperations) - } - - @Test - fun `it returns no request bindings for operations without inputs`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val operation = testModel.expectShape("smithy.example#GetEmptyFoo") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualRequestBindings = unit.requestBindings(operation) - - assertTrue(actualRequestBindings.isEmpty()) - } - - @Test - fun `it returns request bindings for operations with inputs`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val operation = testModel.expectShape("smithy.example#GetFoo") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualRequestBindings = unit.requestBindings(operation) - - assertTrue(actualRequestBindings.size == 1) - val binding = actualRequestBindings.first() - - assertEquals(binding.member.id.toString(), "smithy.example#GetFooInput\$bigInt") - assertEquals(binding.location, HttpBinding.Location.DOCUMENT) - // Location name is unused by awsJson - assertEquals(binding.locationName, null) - } - - @Test - fun `it returns no response bindings for operations without inputs`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val operation = testModel.expectShape("smithy.example#GetEmptyFoo") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualResponseBindings = unit.responseBindings(operation) - - assertTrue(actualResponseBindings.isEmpty()) - } - - @Test - fun `it returns response bindings for operations with inputs`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val operation = testModel.expectShape("smithy.example#GetFoo") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualResponseBindings = unit.responseBindings(operation) - - assertTrue(actualResponseBindings.size == 1) - val binding = actualResponseBindings.first() - - assertEquals(binding.member.id.toString(), "smithy.example#GetFooOutput\$bigInt") - assertEquals(binding.location, HttpBinding.Location.DOCUMENT) - // Location name is unused by awsJson - assertEquals(binding.locationName, null) - } - - @Test - fun `it returns no response bindings for structures without members`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val structure = testModel.expectShape("smithy.example#GetFooError") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualResponseBindings = unit.responseBindings(structure) - - assertTrue(actualResponseBindings.isEmpty()) - } - - @Test - fun `it returns response bindings for structures with members`() { - val (ctx, _, _) = testModel.newTestContext("Example", "smithy.example") - val structure = testModel.expectShape("smithy.example#GetFooOutput") - val unit = AwsJsonHttpBindingResolver(ctx.model, ctx.service, "application/json") - - val actualResponseBindings = unit.responseBindings(structure) - - assertTrue(actualResponseBindings.size == 1) - val binding = actualResponseBindings.first() - - assertEquals(binding.member.id.toString(), "smithy.example#GetFooOutput\$bigInt") - assertEquals(binding.location, HttpBinding.Location.DOCUMENT) - // Location name is unused by awsJson - assertEquals(binding.locationName, null) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGeneratorTest.kt b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGeneratorTest.kt deleted file mode 100644 index 871f1d24811..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/json/AwsJsonProtocolSerdeDescriptorGeneratorTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.json - -import software.amazon.smithy.kotlin.codegen.test.* -import software.amazon.smithy.model.shapes.ShapeId -import kotlin.test.Test - -class AwsJsonProtocolSerdeDescriptorGeneratorTest { - @Test - fun itAddsIgnoreKeysTrait() { - val model = """ - @http(method: "POST", uri: "/foo") - operation Foo { - input: FooRequest - } - - structure FooRequest { - strVal: String, - intVal: Integer - } - - union Bar { - x: String, - y: String, - } - """.prependNamespaceAndService(operations = listOf("Foo")).toSmithyModel() - - val testCtx = model.newTestContext() - val writer = testCtx.newWriter() - val shape = model.expectShape(ShapeId.from("com.test#Bar")) - val renderingCtx = testCtx.toRenderingContext(writer, shape) - - AwsJsonProtocolSerdeDescriptorGenerator(renderingCtx).render() - - val expectedDescriptors = """ - val X_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("x")) - val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("y")) - val OBJ_DESCRIPTOR = SdkObjectDescriptor.build { - trait(IgnoreKey("__type")) - field(X_DESCRIPTOR) - field(Y_DESCRIPTOR) - } - """.formatForTest("") - - val contents = writer.toString() - contents.shouldContainOnlyOnceWithDiff(expectedDescriptors) - } - - @Test - fun itDoesNotAddIgnoreKeysTrait() { - val model = """ - @http(method: "POST", uri: "/foo") - operation Foo { - input: FooRequest - } - - structure FooRequest { - strVal: String, - intVal: Integer - } - - union Bar { - __type: String, - y: String, - } - """.prependNamespaceAndService(operations = listOf("Foo")).toSmithyModel() - - val testCtx = model.newTestContext() - val writer = testCtx.newWriter() - val shape = model.expectShape(ShapeId.from("com.test#Bar")) - val renderingCtx = testCtx.toRenderingContext(writer, shape) - - AwsJsonProtocolSerdeDescriptorGenerator(renderingCtx).render() - - val expectedDescriptors = """ - val TYPE_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("__type")) - val Y_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, JsonSerialName("y")) - val OBJ_DESCRIPTOR = SdkObjectDescriptor.build { - field(TYPE_DESCRIPTOR) - field(Y_DESCRIPTOR) - } - """.formatForTest("") - - val contents = writer.toString() - contents.shouldContainOnlyOnceWithDiff(expectedDescriptors) - } -} diff --git a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGeneratorTest.kt b/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGeneratorTest.kt deleted file mode 100644 index 7da286c7a8b..00000000000 --- a/codegen/smithy-aws-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/aws/protocols/xml/RestXmlSerdeDescriptorGeneratorTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.kotlin.codegen.aws.protocols.xml - -import software.amazon.smithy.kotlin.codegen.test.* -import software.amazon.smithy.model.shapes.ShapeId -import kotlin.test.Test - -class RestXmlSerdeDescriptorGeneratorTest { - private fun render(modelSnippet: String): String { - val model = modelSnippet.prependNamespaceAndService().toSmithyModel() - - val testCtx = model.newTestContext() - val writer = testCtx.newWriter() - val shape = model.expectShape(ShapeId.from("com.test#Foo")) - val renderingCtx = testCtx.toRenderingContext(writer, shape) - - RestXmlSerdeDescriptorGenerator(renderingCtx).render() - return writer.toString() - } - - @Test - fun `it should add alias for message field in error struct`() { - val generated = render( - """ - @error("client") - structure Foo { - message: String, - foo: String - } - """.trimIndent(), - ) - - val expectedDescriptors = """ - val FOO_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, XmlSerialName("foo")) - val MESSAGE_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, XmlSerialName("message"), XmlAliasName("Message")) - """.formatForTest("") - - generated.shouldContainOnlyOnceWithDiff(expectedDescriptors) - } - - @Test - fun `it should not add alias for message field in non-error struct`() { - val generated = render( - """ - structure Foo { - message: String - } - """.trimIndent(), - ) - - val expectedDescriptors = """ - val MESSAGE_DESCRIPTOR = SdkFieldDescriptor(SerialKind.String, XmlSerialName("message")) - """.formatForTest("") - - generated.shouldContainOnlyOnceWithDiff(expectedDescriptors) - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b67f6fc556..8c9bfebf6c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,8 +9,8 @@ coroutines-version = "1.7.3" atomicfu-version = "0.23.1" # smithy-kotlin codegen and runtime are versioned separately -smithy-kotlin-runtime-version = "1.0.18" -smithy-kotlin-codegen-version = "0.30.19" +smithy-kotlin-runtime-version = "1.0.19" +smithy-kotlin-codegen-version = "0.30.20" # codegen smithy-version = "1.45.0" @@ -81,6 +81,7 @@ smithy-kotlin-testing = { module = "aws.smithy.kotlin:testing", version.ref = "s smithy-kotlin-codegen = { module = "software.amazon.smithy.kotlin:smithy-kotlin-codegen", version.ref = "smithy-kotlin-codegen-version" } smithy-kotlin-codegen-testutils = { module = "software.amazon.smithy.kotlin:smithy-kotlin-codegen-testutils", version.ref = "smithy-kotlin-codegen-version" } +smithy-aws-kotlin-codegen = { module = "software.amazon.smithy.kotlin:smithy-aws-kotlin-codegen", version.ref = "smithy-kotlin-codegen-version" } smithy-codegen-core = { module = "software.amazon.smithy:smithy-codegen-core", version.ref = "smithy-version" } smithy-cli = { module = "software.amazon.smithy:smithy-cli", version.ref = "smithy-version" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3a672c596d5..d70d0e64e54 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,7 +32,6 @@ includeBuild("build-support") include(":dokka-aws") include(":bom") include(":codegen:sdk") -include(":codegen:smithy-aws-kotlin-codegen") include(":codegen:aws-sdk-codegen") include(":codegen:protocol-tests") include(":aws-runtime")