Unverified Commit 6326a069 authored by Matteo Bigoi's avatar Matteo Bigoi Committed by GitHub
Browse files

Setup server default RustSettings (#995)



* Setup server default RustSettings. Move extensions save for operation and namespace inside the response or it will be unusable inside other layers

Signed-off-by: default avatarBigo <1781140+crisidev@users.noreply.github.com>

Co-authored-by: default avatardavid-perez <d@vidp.dev>
parent 22f222d9
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -42,9 +42,6 @@ fun generateSmithyBuild(tests: List<CodegenTest>): String {
            "${it.module}": {
                "plugins": {
                    "rust-server-codegen": {
                      "codegen": {
                        "includeFluentClient": false
                      },
                      "runtimeConfig": {
                        "relativePath": "${rootProject.projectDir.absolutePath}/rust-runtime"
                      },
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator:
    ShapeVisitor.Default<Unit>() {

    private val logger = Logger.getLogger(javaClass.name)
    private val settings = RustSettings.from(context.model, context.settings)
    private val settings = ServerRustSettings.from(context.model, context.settings)

    private val symbolProvider: RustSymbolProvider
    private val rustCrate: RustCrate
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

package software.amazon.smithy.rust.codegen.server.smithy

import software.amazon.smithy.model.Model
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.rust.codegen.smithy.CODEGEN_SETTINGS
import software.amazon.smithy.rust.codegen.smithy.CodegenConfig
import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.smithy.RustSettings
import java.util.Optional

/**
 * Configuration of codegen settings
 *
 * [renameExceptions]: Rename `Exception` to `Error` in the generated SDK
 * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to false
 *   and generates its own client)
 *
 * [addMessageToErrors]: Adds a `message` field automatically to all error shapes
 * [formatTimeoutSeconds]: Timeout for running cargo fmt at the end of code generation
 */
data class ServerCodegenConfig(
    val renameExceptions: Boolean = false,
    val includeFluentClient: Boolean = false,
    val addMessageToErrors: Boolean = false,
    val formatTimeoutSeconds: Int = 20,
    // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services
    val eventStreamAllowList: Set<String> = emptySet(),
) {
    companion object {
        fun fromNode(node: Optional<ObjectNode>): CodegenConfig {
            return if (node.isPresent) {
                CodegenConfig.fromNode(node)
            } else {
                CodegenConfig(
                    false,
                    false,
                    false,
                    20,
                    emptySet()
                )
            }
        }
    }
}

/**
 * Settings used by [RustCodegenPlugin]
 */
class ServerRustSettings(
    val service: ShapeId,
    val moduleName: String,
    val moduleVersion: String,
    val moduleAuthors: List<String>,
    val moduleDescription: String?,
    val moduleRepository: String?,
    val runtimeConfig: RuntimeConfig,
    val codegenConfig: CodegenConfig,
    val license: String?,
    val examplesUri: String? = null,
    private val model: Model
) {
    companion object {
        /**
         * Create settings from a configuration object node.
         *
         * @param model Model to infer the service from (if not explicitly set in config)
         * @param config Config object to load
         * @throws software.amazon.smithy.model.node.ExpectationNotMetException
         * @return Returns the extracted settings
         */
        fun from(model: Model, config: ObjectNode): RustSettings {
            val codegenSettings = config.getObjectMember(CODEGEN_SETTINGS)
            val codegenConfig = ServerCodegenConfig.fromNode(codegenSettings)
            return RustSettings.fromCodegenConfig(model, config, codegenConfig)
        }
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -225,7 +225,6 @@ class ServerProtocolTestGenerator(
                checkBody(this, body, httpRequestTestCase)
            }
        }
        checkHttpExtensions(this)

        // Explicitly warn if the test case defined parameters that we aren't doing anything with
        with(httpRequestTestCase) {
@@ -288,6 +287,7 @@ class ServerProtocolTestGenerator(
            );
            """
        )
        checkHttpExtensions(this)
        if (!testCase.body.isEmpty()) {
            rustTemplate(
                """
@@ -328,8 +328,7 @@ class ServerProtocolTestGenerator(
    private fun checkHttpExtensions(rustWriter: RustWriter) {
        rustWriter.rust(
            """
            let extensions = http_request.extensions().expect("unable to extract http request extensions");
            let request_extensions = extensions.get::<aws_smithy_http_server::RequestExtensions>().expect("extension RequestExtensions not found");
            let request_extensions = http_response.extensions().get::<aws_smithy_http_server::RequestExtensions>().expect("extension `RequestExtensions` not found");
            assert_eq!(request_extensions.namespace, ${operationShape.id.getNamespace().dq()});
            assert_eq!(request_extensions.operation_name, ${operationSymbol.name.dq()});
            """.trimIndent()
+7 −8
Original line number Diff line number Diff line
@@ -140,22 +140,19 @@ private class ServerHttpProtocolImplGenerator(
        val operationName = symbolProvider.toSymbol(operationShape).name
        // Implement Axum `FromRequest` trait for input types.
        val inputName = "${operationName}${ServerHttpProtocolGenerator.OPERATION_INPUT_WRAPPER_SUFFIX}"
        val httpExtensions = setHttpExtensions(operationShape)

        val fromRequest = if (operationShape.inputShape(model).hasStreamingMember(model)) {
            // For streaming request bodies, we need to generate a different implementation of the `FromRequest` trait.
            // It will first offer the streaming input to the parser and potentially read the body into memory
            // if an error occurred or if the streaming parser indicates that it needs the full data to proceed.
            """
            async fn from_request(req: &mut #{AxumCore}::extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
                $httpExtensions
            async fn from_request(_req: &mut #{AxumCore}::extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
                todo!("Streaming support for input shapes is not yet supported in `smithy-rs`")
            }
            """.trimIndent()
        } else {
            """
            async fn from_request(req: &mut #{AxumCore}::extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
                $httpExtensions
                Ok($inputName(#{parse_request}(req).await?))
            }
            """.trimIndent()
@@ -184,6 +181,7 @@ private class ServerHttpProtocolImplGenerator(
        val outputName = "${operationName}${ServerHttpProtocolGenerator.OPERATION_OUTPUT_WRAPPER_SUFFIX}"
        val errorSymbol = operationShape.errorSymbol(symbolProvider)

        val httpExtensions = setHttpExtensions(operationShape)
        // For streaming response bodies, we need to generate a different implementation of the `IntoResponse` trait.
        // The body type will have to be a `StreamBody`. The service implementer will return a `Stream` from their handler.
        val intoResponseStreaming = "todo!(\"Streaming support for output shapes is not yet supported in `smithy-rs`\")"
@@ -192,7 +190,7 @@ private class ServerHttpProtocolImplGenerator(
                intoResponseStreaming
            } else {
                """
                match self {
                let mut response = match self {
                    Self::Output(o) => {
                        match #{serialize_response}(&o) {
                            Ok(response) => response,
@@ -212,7 +210,9 @@ private class ServerHttpProtocolImplGenerator(
                            }
                        }
                    }
                }
                };
                $httpExtensions
                response
                """.trimIndent()
            }
            // The output of fallible operations is a `Result` which we convert into an isomorphic `enum` type we control
@@ -314,8 +314,7 @@ private class ServerHttpProtocolImplGenerator(
        val namespace = operationShape.id.getNamespace()
        val operationName = symbolProvider.toSymbol(operationShape).name
        return """
            let extensions = req.extensions_mut().ok_or(#{SmithyHttpServer}::rejection::ExtensionsAlreadyExtracted)?;
            extensions.insert(#{SmithyHttpServer}::RequestExtensions::new(${namespace.dq()}, ${operationName.dq()}));
            response.extensions_mut().insert(#{SmithyHttpServer}::RequestExtensions::new(${namespace.dq()}, ${operationName.dq()}));
        """.trimIndent()
    }

Loading