Unverified Commit 53dcff3f authored by david-perez's avatar david-perez Committed by GitHub
Browse files

Build services with a derived configuration object (#3095)

This is a pared-down version of #2809.

This is the code-generated version of the
david-perez/smithy-rs-service-config#2 POC.

This introduces a code-generated `PokemonServiceConfig` object which is
provided to the service builder constructor via
`PokemonService::builder(config: PokemonServiceConfig)`. This will
displace the
current `builder_without_plugins` and `builder_with_plugins`,
deprecating them.
We will eventually remove them.

The motivation is to have a single place where to register plugins and
layers.
Smithy traits can leverage this object to inject methods that can apply
plugins
and layers. For example, an `@authorization` trait implementer could
vend a
smithy-rs decorator that pre-applied an authorization plugin inside the
`${serviceName}Config` object, possibly exposing a method to pass in any
runtime arguments needed to configure the middleware.

## Checklist
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 06e265df
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -506,3 +506,9 @@ The [`connection`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/connec
references = ["smithy-rs#3092", "smithy-rs#3093"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "ysaito1001"

[[smithy-rs]]
message = "Service builder initialization now takes in a `${serviceName}Config` object on which plugins and layers should be registered. The `builder_with_plugins` and `builder_without_plugins` methods on the service builder, as well as the `layer` method on the built service have been deprecated, and will be removed in a future release. See the [upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/3096) for more details."
references = ["smithy-rs#3095", "smithy-rs#3096"]
meta = { "breaking" = true, "tada" = false, "bug" = true, "target" = "server" }
author = "david-perez"
+1 −4
Original line number Diff line number Diff line
@@ -551,10 +551,7 @@ class RustWriter private constructor(
        if (this.className.contains("AbstractCodeWriter") || this.className.startsWith("java.lang")) {
            return false
        }
        if (this.fileName == "RustWriter.kt") {
            return false
        }
        return true
        return this.fileName != "RustWriter.kt"
    }

    private val preamble = mutableListOf<Writable>()
+7 −4
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerRootGe
import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerRuntimeTypesReExportsGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl
import software.amazon.smithy.rust.codegen.server.smithy.generators.ServiceConfigGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedCollectionGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator
import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator
@@ -590,7 +591,7 @@ open class ServerCodegenVisitor(
        logger.info("[rust-server-codegen] Generating a service $shape")
        val serverProtocol = protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol

        // Generate root
        // Generate root.
        rustCrate.lib {
            ServerRootGenerator(
                serverProtocol,
@@ -598,21 +599,23 @@ open class ServerCodegenVisitor(
            ).render(this)
        }

        // Generate server re-exports
        // Generate server re-exports.
        rustCrate.withModule(ServerRustModule.Server) {
            ServerRuntimeTypesReExportsGenerator(codegenContext).render(this)
        }

        // Generate protocol tests
        // Generate protocol tests.
        protocolTests()

        // Generate service module
        // Generate service module.
        rustCrate.withModule(ServerRustModule.Service) {
            ServerServiceGenerator(
                codegenContext,
                serverProtocol,
            ).render(this)

            ServiceConfigGenerator(codegenContext).render(this)

            ScopeMacroGenerator(codegenContext).render(this)
        }
    }
+16 −17
Original line number Diff line number Diff line
@@ -73,9 +73,9 @@ open class ServerRootGenerator(
            //! ```rust,no_run
            //! ## use std::net::SocketAddr;
            //! ## async fn dummy() {
            //! use $crateName::$serviceName;
            //! use $crateName::{$serviceName, ${serviceName}Config};
            //!
            //! ## let app = $serviceName::builder_without_plugins().build_unchecked();
            //! ## let app = $serviceName::builder(${serviceName}Config::builder().build()).build_unchecked();
            //! let server = app.into_make_service();
            //! let bind: SocketAddr = "127.0.0.1:6969".parse()
            //!     .expect("unable to parse the server bind address and port");
@@ -92,7 +92,7 @@ open class ServerRootGenerator(
            //! use $crateName::$serviceName;
            //!
            //! ## async fn dummy() {
            //! ## let app = $serviceName::builder_without_plugins().build_unchecked();
            //! ## let app = $serviceName::builder(${serviceName}Config::builder().build()).build_unchecked();
            //! let handler = LambdaHandler::new(app);
            //! lambda_http::run(handler).await.unwrap();
            //! ## }
@@ -100,28 +100,26 @@ open class ServerRootGenerator(
            //!
            //! ## Building the $serviceName
            //!
            //! To construct [`$serviceName`] we use [`$builderName`] returned by [`$serviceName::builder_without_plugins`]
            //! or [`$serviceName::builder_with_plugins`].
            //! To construct [`$serviceName`] we use [`$builderName`] returned by [`$serviceName::builder`].
            //!
            //! #### Plugins
            //!
            //! The [`$serviceName::builder_with_plugins`] method, returning [`$builderName`],
            //! accepts a plugin marked with [`HttpMarker`](aws_smithy_http_server::plugin::HttpMarker) and a
            //! plugin marked with [`ModelMarker`](aws_smithy_http_server::plugin::ModelMarker).
            //! The [`$serviceName::builder`] method, returning [`$builderName`],
            //! accepts a config object on which plugins can be registered.
            //! Plugins allow you to build middleware which is aware of the operation it is being applied to.
            //!
            //! ```rust
            //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin;
            //! ```rust,no_run
            //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin;
            //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin;
            //! ## use #{Hyper}::Body;
            //! use #{SmithyHttpServer}::plugin::HttpPlugins;
            //! use $crateName::{$serviceName, $builderName};
            //! use $crateName::{$serviceName, ${serviceName}Config, $builderName};
            //!
            //! let http_plugins = HttpPlugins::new()
            //!         .push(LoggingPlugin)
            //!         .push(MetricsPlugin);
            //! let builder: $builderName<Body, _, _> = $serviceName::builder_with_plugins(http_plugins, IdentityPlugin);
            //! let config = ${serviceName}Config::builder().build();
            //! let builder: $builderName<Body, _, _, _> = $serviceName::builder(config);
            //! ```
            //!
            //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins.
@@ -136,7 +134,7 @@ open class ServerRootGenerator(
            //! * A `Result<Output, Error>` if your operation has modeled errors, or
            //! * An `Output` otherwise.
            //!
            //! ```rust
            //! ```rust,no_run
            //! ## struct Input;
            //! ## struct Output;
            //! ## struct Error;
@@ -147,7 +145,7 @@ open class ServerRootGenerator(
            //!
            //! Handlers can accept up to 8 extractors:
            //!
            //! ```rust
            //! ```rust,no_run
            //! ## struct Input;
            //! ## struct Output;
            //! ## struct Error;
@@ -187,11 +185,12 @@ open class ServerRootGenerator(
            //!
            //! ```rust
            //! ## use std::net::SocketAddr;
            //! use $crateName::$serviceName;
            //! use $crateName::{$serviceName, ${serviceName}Config};
            //!
            //! ##[#{Tokio}::main]
            //! pub async fn main() {
            //!    let app = $serviceName::builder_without_plugins()
            //!    let config = ${serviceName}Config::builder().build();
            //!    let app = $serviceName::builder(config)
            ${builderFieldNames.values.joinToString("\n") { "//!        .$it($it)" }}
            //!        .build()
            //!        .expect("failed to build an instance of $serviceName");
@@ -237,6 +236,6 @@ open class ServerRootGenerator(
    fun render(rustWriter: RustWriter) {
        documentation(rustWriter)

        rustWriter.rust("pub use crate::service::{$serviceName, ${serviceName}Builder, MissingOperationsError};")
        rustWriter.rust("pub use crate::service::{$serviceName, ${serviceName}Config, ${serviceName}ConfigBuilder, ${serviceName}Builder, MissingOperationsError};")
    }
}
Loading