Unverified Commit 15e21af3 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Protocol unification & Add Rest XML deserialization support (#369)

* Refactor HttpBinding protocol

* Update XmlBindingTraitParserGenerator to fix new protocol tests

* Clear all warnings

* Major overhaul of Http Trait based protocols

* Remove pointless wrapping

* Nullable types fall through

* Cleanup JsonParserGenerator, refactor

* More Json parser generator cleanups

* Update test

* Fix unit test

* [cleanup] Move interfaces to their own files

* Use parser / serializer generators from AwsJson
parent 47702e45
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -40,6 +40,10 @@ val CodegenTests = listOf(
        "aws.protocoltests.restjson#RestJsonExtras",
        "rest_json_extas"
    ),
    CodegenTest(
        "aws.protocoltests.restxml#RestXml",
        "rest_xml"
    ),
    CodegenTest(
        "crate#Config",
        "naming_test", """
+1 −25
Original line number Diff line number Diff line
@@ -53,33 +53,9 @@ apply QueryPrecedence @httpRequestTests([
@restJson1
service RestJsonExtras {
    version: "2019-12-16",
    operations: [EnumPayload, StringPayload, PrimitiveIntHeader, EnumQuery, StatusResponse]
    operations: [StringPayload, PrimitiveIntHeader, EnumQuery, StatusResponse]
}

@http(uri: "/EnumPayload", method: "POST")
@httpRequestTests([
    {
        id: "EnumPayload",
        uri: "/EnumPayload",
        body: "enumvalue",
        params: { payload: "enumvalue" },
        method: "POST",
        protocol: "aws.protocols#restJson1"
    }
])
operation EnumPayload {
    input: EnumPayloadInput,
    output: EnumPayloadInput
}

structure EnumPayloadInput {
    @httpPayload
    payload: StringEnum
}

@enum([{"value": "enumvalue", "name": "V"}])
string StringEnum

@http(uri: "/StringPayload", method: "POST")
@httpRequestTests([
    {
+5 −0
Original line number Diff line number Diff line
@@ -101,6 +101,11 @@ class InlineDependency(
            CargoDependency.Serde,
            CargoDependency.SmithyHttp(runtimeConfig)
        )

        fun wrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency =
            forRustFile("rest_xml_wrapped_errors", CargoDependency.smithyXml(runtimeConfig))
        fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency =
            forRustFile("rest_xml_unwrapped_errors", CargoDependency.smithyXml(runtimeConfig))
    }
}

+4 −1
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
        fun SerdeJson(path: String) =
            RuntimeType(path, dependency = CargoDependency.SerdeJson, namespace = "serde_json")

        val SJ = RuntimeType(null, dependency = CargoDependency.SerdeJson, namespace = "serde_json")
        val serdeJson = RuntimeType(null, dependency = CargoDependency.SerdeJson, namespace = "serde_json")

        fun awsJsonErrors(runtimeConfig: RuntimeConfig) =
            forInlineDependency(InlineDependency.awsJsonErrors(runtimeConfig))
@@ -200,5 +200,8 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
            dependency = CargoDependency.SmithyHttp(runtimeConfig),
            namespace = "smithy_http::response"
        )

        fun wrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.wrappedXmlErrors(runtimeConfig))
        fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig))
    }
}
+4 −41
Original line number Diff line number Diff line
@@ -24,12 +24,9 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization
import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection
import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol
import software.amazon.smithy.rust.codegen.smithy.letIf
import software.amazon.smithy.rust.codegen.util.dq
import software.amazon.smithy.rust.codegen.util.hasStreamingMember
import software.amazon.smithy.rust.codegen.util.inputShape
import software.amazon.smithy.rust.codegen.util.outputShape

/**
 * Configuration needed to generate the client for a given Service<->Protocol pair
@@ -111,19 +108,7 @@ abstract class HttpProtocolGenerator(
        operationWriter.implBlock(operationShape, symbolProvider) {
            builderGenerator.renderConvenienceMethod(this)

            val responseTypes = responseBody(operationShape)
            val mutability = responseTypes.mutability
            val type = responseTypes.type
            fromResponseImpl(this, operationShape)

            rustBlock(
                "fn parse_response(&self, $mutability response: &$mutability #T<$type>) -> Result<#T, #T>",
                RuntimeType.Http("response::Response"),
                symbolProvider.toSymbol(operationShape.outputShape(model)),
                operationShape.errorSymbol(symbolProvider)
            ) {
                write("Self::from_response(&$mutability response)")
            }
            operationImplBlock(this, operationShape)

            rustBlock("pub fn new() -> Self") {
                rust("Self { _private: () }")
@@ -136,13 +121,6 @@ abstract class HttpProtocolGenerator(

    data class ResponseBody(val type: String, val mutability: String)

    private fun RustWriter.responseBody(operationShape: OperationShape): ResponseBody {
        return when (operationShape.outputShape(model).hasStreamingMember(model)) {
            true -> ResponseBody(this.format(RuntimeType.sdkBody(protocolConfig.runtimeConfig)), "mut")
            false -> ResponseBody("impl AsRef<[u8]>", "")
        }
    }

    protected fun httpBuilderFun(implBlockWriter: RustWriter, f: RustWriter.() -> Unit) {
        Attribute.Custom("allow(clippy::unnecessary_wraps)").render(implBlockWriter)
        implBlockWriter.rustBlock(
@@ -154,24 +132,8 @@ abstract class HttpProtocolGenerator(
    }

    data class BodyMetadata(val takesOwnership: Boolean)
    abstract fun RustWriter.body(self: String, operationShape: OperationShape): BodyMetadata

    protected fun fromResponseFun(
        implBlockWriter: RustWriter,
        operationShape: OperationShape,
        block: RustWriter.() -> Unit
    ) {
        Attribute.Custom("allow(clippy::unnecessary_wraps)").render(implBlockWriter)
        val responseBodyType = implBlockWriter.responseBody(operationShape)
        implBlockWriter.rustBlock(
            "fn from_response(response: & ${responseBodyType.mutability} #T<${responseBodyType.type}>) -> Result<#T, #T>",
            RuntimeType.Http("response::Response"),
            symbolProvider.toSymbol(operationShape.outputShape(model)),
            operationShape.errorSymbol(symbolProvider)
        ) {
            block(this)
        }
    }
    abstract fun RustWriter.body(self: String, operationShape: OperationShape): BodyMetadata

    private fun buildOperation(
        implBlockWriter: RustWriter,
@@ -234,7 +196,8 @@ abstract class HttpProtocolGenerator(

    abstract fun traitImplementations(operationWriter: RustWriter, operationShape: OperationShape)

    abstract fun fromResponseImpl(implBlockWriter: RustWriter, operationShape: OperationShape)
    /** Write code into the impl block for [operationShape] */
    open fun operationImplBlock(implBlockWriter: RustWriter, operationShape: OperationShape) {}

    /**
     * Add necessary methods to the impl block for the input shape.
Loading