Unverified Commit 17cb98c9 authored by Harry Barber's avatar Harry Barber Committed by GitHub
Browse files

Unhide new service builder and deprecate the prior (#1886)



Co-authored-by: default avatardavid-perez <d@vidp.dev>
parent b7f1a579
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
@@ -502,3 +502,108 @@ in non-serverless environments (e.g. via `hyper`).
references = ["smithy-rs#2035"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" }
author = "LukeMathWalker"

[[smithy-rs]]
message = """
### Plugins/New Service Builder API

The `Router` struct has been replaced by a new `Service` located at the root of the generated crate. Its name coincides with the same name as the Smithy service you are generating.

```rust
use pokemon_service_server_sdk::PokemonService;
```

The new service builder infrastructure comes with a `Plugin` system which supports middleware on `smithy-rs`. See the [mididleware documentation](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/middleware.md) and the [API documentation](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/plugin/index.html) for more details.

Usage of the new service builder API:

```rust
// Apply a sequence of plugins using `PluginPipeline`.
let plugins = PluginPipeline::new()
    // Apply the `PrintPlugin`.
    // This is a dummy plugin found in `rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs`
    .print()
    // Apply the `InstrumentPlugin` plugin, which applies `tracing` instrumentation.
    .instrument();

// Construct the service builder using the `plugins` defined above.
let app = PokemonService::builder_with_plugins(plugins)
    // Assign all the handlers.
    .get_pokemon_species(get_pokemon_species)
    .get_storage(get_storage)
    .get_server_statistics(get_server_statistics)
    .capture_pokemon(capture_pokemon)
    .do_nothing(do_nothing)
    .check_health(check_health)
    // Construct the `PokemonService`.
    .build()
    // If handlers are missing a descriptive error will be provided.
    .expect("failed to build an instance of `PokemonService`");
```

See the `rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin` folder for various working examples.

### Public `FromParts` trait

Previously, we only supported one [`Extension`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/struct.Extension.html) as an additional argument provided to the handler. This number has been increased to 8 and the argument type has been broadened to any struct which implements the [`FromParts`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/trait.FromParts.html) trait. The trait is publicly exported and therefore provides customers with the ability to extend the domain of the handlers.

As noted, a ubiqutious example of a struct that implements `FromParts` is the `Extension` struct, which extracts state from the `Extensions` typemap of a [`http::Request`](https://docs.rs/http/latest/http/request/struct.Request.html). A new example is the `ConnectInfo` struct which allows handlers to access the connection data. See the `rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs` example.

```rust
fn get_pokemon_species(
    input: GetPokemonSpeciesInput,
    state: Extension<State>,
    address: ConnectInfo<SocketAddr>
) -> Result<GetPokemonSpeciesOutput, GetPokemonSpeciesError> {
    todo!()
}
```

In addition to the [`ConnectInfo`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/connect_info/struct.ConnectInfo.html) extractor, we also have added [lambda extractors](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/lambda/index.html) which are feature gated with `aws-lambda`.

[`FromParts` documentation](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/from_parts.md) has been added.

### New Documentation

New sections to have been added to the [server side of the book](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/overview.md).

These include:

- [Middleware](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/middleware.md)
- [Accessing Un-modelled Data](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/from_parts.md)
- [Anatomy of a Service](https://github.com/awslabs/smithy-rs/blob/main/design/src/server/anatomy.md)

This release also introduces extensive documentation at the root of the generated crate. For best results compile documentation with `cargo +nightly doc --open`.

### Deprecations

The existing service builder infrastructure, `OperationRegistryBuilder`/`OperationRegistry`/`Router`, is now deprecated. Customers should migrate to the newer scheme described above. The deprecated types will be removed in a future release.
"""
references = [
    "smithy-rs#1620",
    "smithy-rs#1666",
    "smithy-rs#1731",
    "smithy-rs#1736",
    "smithy-rs#1753",
    "smithy-rs#1738",
    "smithy-rs#1782",
    "smithy-rs#1829",
    "smithy-rs#1837",
    "smithy-rs#1891",
    "smithy-rs#1840",
    "smithy-rs#1844",
    "smithy-rs#1858",
    "smithy-rs#1930",
    "smithy-rs#1999",
    "smithy-rs#2003",
    "smithy-rs#2008",
    "smithy-rs#2010",
    "smithy-rs#2019",
    "smithy-rs#2020",
    "smithy-rs#2021",
    "smithy-rs#2038",
    "smithy-rs#2039",
    "smithy-rs#2041",
]
meta = { "breaking" = true, "tada" = true, "bug" = false, "target" = "server" }
author = "hlbarber"
+22 −0
Original line number Diff line number Diff line
@@ -424,6 +424,28 @@ sealed class Attribute {
        val NonExhaustive = Custom("non_exhaustive")
    }

    data class Deprecated(val since: String?, val note: String?) : Attribute() {
        override fun render(writer: RustWriter) {
            writer.raw("#[deprecated")
            if (since != null || note != null) {
                writer.raw("(")
                if (since != null) {
                    writer.raw("""since = "$since"""")

                    if (note != null) {
                        writer.raw(", ")
                    }
                }

                if (note != null) {
                    writer.raw("""note = "$note"""")
                }
                writer.raw(")")
            }
            writer.raw("]")
        }
    }

    data class Derives(val derives: Set<RuntimeType>) : Attribute() {
        override fun render(writer: RustWriter) {
            if (derives.isEmpty()) {
+2 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ ${operationImplementationStubs(operations)}
    }

    private fun renderOperationRegistryStruct(writer: RustWriter) {
        writer.rust("""##[deprecated(since = "0.52.0", note = "`OperationRegistry` is part of the deprecated service builder API. Use `$serviceName::builder` instead.")]""")
        writer.rustBlock("pub struct $operationRegistryNameWithArguments") {
            val members = operationNames
                .mapIndexed { i, operationName -> "$operationName: Op$i" }
@@ -182,6 +183,7 @@ ${operationImplementationStubs(operations)}
     * Renders the `OperationRegistryBuilder` structure, used to build the `OperationRegistry`.
     */
    private fun renderOperationRegistryBuilderStruct(writer: RustWriter) {
        writer.rust("""##[deprecated(since = "0.52.0", note = "`OperationRegistryBuilder` is part of the deprecated service builder API. Use `$serviceName::builder` instead.")]""")
        writer.rustBlock("pub struct $operationRegistryBuilderNameWithArguments") {
            val members = operationNames
                .mapIndexed { i, operationName -> "$operationName: Option<Op$i>" }
+17 −16
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.InputsModule
import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol
@@ -69,8 +70,8 @@ open class ServerServiceGenerator(
            //!
            //! ## Using $serviceName
            //!
            //! The primary entrypoint is [`$serviceName`]: it satisfies the [`Service<http::Request, Response = http::Response>`]
            //! trait and therefore can be handed to a [`hyper` server] via [`$serviceName::into_make_service`] or used in Lambda via [`LambdaHandler`](#{SmithyHttpServer}::routing::LambdaHandler).
            //! The primary entrypoint is [`$serviceName`]: it satisfies the [`Service<http::Request, Response = http::Response>`](#{Tower}::Service)
            //! trait and therefore can be handed to a [`hyper` server](https://github.com/hyperium/hyper) via [`$serviceName::into_make_service`] or used in Lambda via [`LambdaHandler`](#{SmithyHttpServer}::routing::LambdaHandler).
            //! The [`crate::${InputsModule.name}`], ${if (!hasErrors) "and " else ""}[`crate::${OutputsModule.name}`], ${if (hasErrors) "and [`crate::${ErrorsModule.name}`]" else "" }
            //! modules provide the types used in each operation.
            //!
@@ -225,6 +226,7 @@ open class ServerServiceGenerator(
            "Handlers" to handlers,
            "ExampleHandler" to operations.take(1).map { operation -> DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() },
            "SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(codegenContext.runtimeConfig).toType(),
            "Tower" to ServerCargoDependency.Tower.toType(),
        )
    }

@@ -236,7 +238,6 @@ open class ServerServiceGenerator(
        rustCrate.lib {
            documentation(this)

            rust("##[doc(inline, hidden)]")
            rust("pub use crate::service::{$serviceName, ${serviceName}Builder, MissingOperationsError};")
        }

@@ -255,35 +256,35 @@ open class ServerServiceGenerator(
            renderOperationHandler(this, operations)
        }
        rustCrate.withModule(
            RustModule.public(
            RustModule.LeafModule(
                "operation_registry",
                RustMetadata(
                    visibility = Visibility.PUBLIC,
                    additionalAttributes = listOf(
                        Attribute.Deprecated("0.52.0", "This module exports the deprecated `OperationRegistry`. Use the service builder exported from your root crate."),
                    ),
                ),
                """
                Contains the [`operation_registry::OperationRegistry`], a place where
                you can register your service's operation implementations.

                ## Deprecation

                This service builder is deprecated - use [`${codegenContext.serviceShape.id.name.toPascalCase()}::builder_with_plugins`] or [`${codegenContext.serviceShape.id.name.toPascalCase()}::builder_without_plugins`] instead.
                """,
            ),
        ) {
            renderOperationRegistry(this, operations)
        }

        // TODO(https://github.com/awslabs/smithy-rs/issues/1707): Remove, this is temporary.
        rustCrate.withModule(
            RustModule.LeafModule(
                "operation_shape",
                RustMetadata(
                    visibility = Visibility.PUBLIC,
                    additionalAttributes = listOf(
                        Attribute.DocHidden,
                    ),
                ),
            ),
            RustModule.public("operation_shape"),
        ) {
            ServerOperationShapeGenerator(operations, codegenContext).render(this)
        }

        // TODO(https://github.com/awslabs/smithy-rs/issues/1707): Remove, this is temporary.
        rustCrate.withModule(
            RustModule.LeafModule("service", RustMetadata(visibility = Visibility.PRIVATE, additionalAttributes = listOf(Attribute.DocHidden)), null),
            RustModule.private("service"),
        ) {
            ServerServiceGeneratorV2(
                codegenContext,
+2 −1
Original line number Diff line number Diff line
@@ -379,6 +379,7 @@ class ServerServiceGeneratorV2(
                    #{SmithyHttpServer}::routing::IntoMakeService::new(self)
                }


                /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService) with [`ConnectInfo`](#{SmithyHttpServer}::request::connect_info::ConnectInfo).
                pub fn into_make_service_with_connect_info<C>(self) -> #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo<Self, C> {
                    #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo::new(self)
@@ -396,7 +397,7 @@ class ServerServiceGeneratorV2(

                /// Applies [`Route::new`](#{SmithyHttpServer}::routing::Route::new) to all routes.
                ///
                /// This has the effect of erasing all types accumulated via [`layer`].
                /// This has the effect of erasing all types accumulated via [`layer`]($serviceName::layer).
                pub fn boxed<B>(self) -> $serviceName<#{SmithyHttpServer}::routing::Route<B>>
                where
                    S: #{Tower}::Service<
Loading