From 17cb98c9750426b9f5318672428531d74e9610f7 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:31:00 +0000 Subject: [PATCH] Unhide new service builder and deprecate the prior (#1886) Co-authored-by: david-perez --- CHANGELOG.next.toml | 105 ++++++++++++++++++ .../rust/codegen/core/rustlang/RustType.kt | 22 ++++ .../ServerOperationRegistryGenerator.kt | 2 + .../generators/ServerServiceGenerator.kt | 33 +++--- .../generators/ServerServiceGeneratorV2.kt | 3 +- design/src/SUMMARY.md | 10 +- design/src/server/instrumentation.md | 14 ++- design/src/server/overview.md | 8 +- .../src/bin/pokemon-service-connect-info.rs | 4 +- .../src/bin/pokemon-service-lambda.rs | 44 +++++--- .../src/bin/pokemon-service-tls.rs | 23 ++-- .../src/bin/pokemon-service.rs | 23 ++-- .../examples/pokemon-service/src/lib.rs | 3 +- .../examples/pokemon-service/src/plugin.rs | 4 +- .../aws-smithy-http-server/src/extension.rs | 12 ++ .../aws-smithy-http-server/src/lib.rs | 5 +- .../src/operation/mod.rs | 2 +- .../aws-smithy-http-server/src/protocols.rs | 5 + .../aws-smithy-http-server/src/rejection.rs | 8 ++ .../aws-smithy-http-server/src/request/mod.rs | 5 + .../aws-smithy-http-server/src/response.rs | 1 - .../src/{routers/mod.rs => routers.rs} | 0 .../src/routing/future.rs | 6 + .../aws-smithy-http-server/src/routing/mod.rs | 11 ++ .../src/runtime_error.rs | 1 + .../src/server_operation_handler_trait.rs | 15 +++ 26 files changed, 289 insertions(+), 80 deletions(-) rename rust-runtime/aws-smithy-http-server/src/{routers/mod.rs => routers.rs} (100%) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 3b6d0210a1..833c6d13b0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -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, + address: ConnectInfo +) -> Result { + 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" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 4e11257059..9654fa966c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -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) : Attribute() { override fun render(writer: RustWriter) { if (derives.isEmpty()) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt index 18dfbd869d..1c2b459f53 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt @@ -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" } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index c8bcb5250f..0e720d00f1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -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`] - //! 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`](#{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, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt index e0c852b6e7..824761cf25 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt @@ -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(self) -> #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo { #{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(self) -> $serviceName<#{SmithyHttpServer}::routing::Route> where S: #{Tower}::Service< diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index 4f745d310a..cdb5c08aa7 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -15,10 +15,12 @@ - [Backwards Compatibility](smithy/backwards-compat.md) - [Server](./server/overview.md) - - [Generating Common Service Code](./server/code_generation.md) - - [Generating the Pokémon Service](./server/pokemon_service.md) - - [Instrumentation](./server/instrumentation.md) - + - [Middleware](./middleware.md) + - [Instrumentation](./instrumentation.md) + - [Accessing Un-modelled Data](./from_parts.md) + - [The Anatomy of a Service](./anatomy.md) + - [Generating Common Service Code](./code_generation.md) + - [Generating the Pokémon Service](./pokemon_service.md) - [RFCs](./rfcs/overview.md) - [RFC-0001: Sharing configuration between multiple clients](./rfcs/rfc0001_shared_config.md) diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index ff6e691389..31eef1c38b 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -53,11 +53,19 @@ Smithy provides an out-the-box middleware which: - Opens a DEBUG level span, prior to request handling, including the operation name and request URI and headers. - Emits a DEBUG level event, after to request handling, including the response headers and status code. -This is applied by default and can be enabled and disabled by filtering on `aws_smithy_http_server::instrumentation`. +This is enabled via the `instrument` method provided by the `aws_smithy_http_server::instrumentation::InstrumentExt` trait. - +```rust +use aws_smithy_http_server::instrumentation::InstrumentExt; - +let plugins = PluginPipeline::new().instrument(); +let app = PokemonService::builder_with_plugins(plugins) + .get_pokemon_species(/* handler */) + /* ... */ + .build(); +``` + + ### Example diff --git a/design/src/server/overview.md b/design/src/server/overview.md index 44d177f3f4..65cff8d606 100644 --- a/design/src/server/overview.md +++ b/design/src/server/overview.md @@ -2,9 +2,9 @@ Smithy Rust provides the ability to generate a server whose operations are provided by the customer. +- [Middleware](./middleware.md) +- [Instrumentation](./instrumentation.md) +- [Accessing Un-modelled Data](./from_parts.md) +- [The Anatomy of a Service](./anatomy.md) - [Generating Common Service Code](./code_generation.md) - [Generating the Pokémon Service](./pokemon_service.md) - -- [Instrumentation](./instrumentation.md) - - diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs index 8d17756ff1..c366f747db 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-connect-info.rs @@ -70,10 +70,10 @@ async fn main() { .expect("failed to build an instance of PokemonService"); // Start the [`hyper::Server`]. - let bind: std::net::SocketAddr = format!("{}:{}", args.address, args.port) + let bind: SocketAddr = format!("{}:{}", args.address, args.port) .parse() .expect("unable to parse the server bind address and port"); - let server = hyper::Server::bind(&bind).serve(app.into_make_service_with_connect_info::()); + let server = hyper::Server::bind(&bind).serve(app.into_make_service_with_connect_info::()); // Run forever-ish... if let Err(err) = server.await { diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs index 422451b312..2f71de5a51 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-lambda.rs @@ -6,36 +6,50 @@ // This program is exported as a binary named `pokemon-service-lambda`. use std::sync::Arc; -use aws_smithy_http_server::{routing::LambdaHandler, AddExtensionLayer, Router}; +use aws_smithy_http_server::{ + plugin::PluginPipeline, request::lambda::Context, routing::LambdaHandler, AddExtensionLayer, Extension, +}; use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, setup_tracing, - State, + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, plugin::PrintExt, + setup_tracing, State, }; -use pokemon_service_server_sdk::operation_registry::OperationRegistryBuilder; +use pokemon_service_server_sdk::{error, input, output, PokemonService}; + +/// Retrieves the user's storage and records the . +pub async fn get_storage_lambda( + input: input::GetStorageInput, + _state: Extension>, + context: Context, +) -> Result { + tracing::debug!(request_id = %context.request_id, "attempting to authenticate storage user"); + + // We currently only support Ash and he has nothing stored + if !(input.user == "ash" && input.passcode == "pikachu123") { + tracing::debug!("authentication failed"); + return Err(error::GetStorageError::NotAuthorized(error::NotAuthorized {})); + } + Ok(output::GetStorageOutput { collection: vec![] }) +} #[tokio::main] pub async fn main() { setup_tracing(); - - let app: Router = OperationRegistryBuilder::default() + // Apply the `PrintPlugin` defined in `plugin.rs` + let plugins = PluginPipeline::new().print(); + let app = PokemonService::builder_with_plugins(plugins) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and // return the operation's output. .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage) + .get_storage(get_storage_lambda) .get_server_statistics(get_server_statistics) .capture_pokemon(capture_pokemon) .do_nothing(do_nothing) .check_health(check_health) .build() - .expect("Unable to build operation registry") - // Convert it into a router that will route requests to the matching operation - // implementation. - .into(); - - // Setup shared state and middlewares. - let shared_state = Arc::new(State::default()); - let app = app.layer(AddExtensionLayer::new(shared_state)); + .expect("failed to build an instance of PokemonService") + // Set up shared state and middlewares. + .layer(&AddExtensionLayer::new(Arc::new(State::default()))); let handler = LambdaHandler::new(app); let lambda = lambda_http::run(handler); diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs index aa891131c0..34ccd0c6f1 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service-tls.rs @@ -28,14 +28,14 @@ use std::io::BufReader; use std::net::SocketAddr; use std::sync::Arc; -use aws_smithy_http_server::{AddExtensionLayer, Router}; +use aws_smithy_http_server::{plugin::PluginPipeline, AddExtensionLayer}; use clap::Parser; use futures_util::stream::StreamExt; use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, setup_tracing, - State, + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, + plugin::PrintExt, setup_tracing, State, }; -use pokemon_service_server_sdk::operation_registry::OperationRegistryBuilder; +use pokemon_service_server_sdk::PokemonService; use tokio_rustls::{ rustls::{Certificate, PrivateKey, ServerConfig}, TlsAcceptor, @@ -62,7 +62,9 @@ struct Args { pub async fn main() { let args = Args::parse(); setup_tracing(); - let app: Router = OperationRegistryBuilder::default() + // Apply the `PrintPlugin` defined in `plugin.rs` + let plugins = PluginPipeline::new().print(); + let app = PokemonService::builder_with_plugins(plugins) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and // return the operation's output. @@ -73,14 +75,9 @@ pub async fn main() { .do_nothing(do_nothing) .check_health(check_health) .build() - .expect("Unable to build operation registry") - // Convert it into a router that will route requests to the matching operation - // implementation. - .into(); - - // Setup shared state and middlewares. - let shared_state = Arc::new(State::default()); - let app = app.layer(AddExtensionLayer::new(shared_state)); + .expect("failed to build an instance of PokemonService") + // Set up shared state and middlewares. + .layer(&AddExtensionLayer::new(Arc::new(State::default()))); let addr: SocketAddr = format!("{}:{}", args.address, args.port) .parse() diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs index b6df4770fd..8c68643e3b 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/bin/pokemon-service.rs @@ -6,13 +6,13 @@ // This program is exported as a binary named `pokemon-service`. use std::{net::SocketAddr, sync::Arc}; -use aws_smithy_http_server::{AddExtensionLayer, Router}; +use aws_smithy_http_server::{plugin::PluginPipeline, AddExtensionLayer}; use clap::Parser; use pokemon_service::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, setup_tracing, - State, + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, get_storage, + plugin::PrintExt, setup_tracing, State, }; -use pokemon_service_server_sdk::operation_registry::OperationRegistryBuilder; +use pokemon_service_server_sdk::PokemonService; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -29,7 +29,9 @@ struct Args { pub async fn main() { let args = Args::parse(); setup_tracing(); - let app: Router = OperationRegistryBuilder::default() + // Apply the `PrintPlugin` defined in `plugin.rs` + let plugins = PluginPipeline::new().print(); + let app = PokemonService::builder_with_plugins(plugins) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and // return the operation's output. @@ -40,14 +42,9 @@ pub async fn main() { .do_nothing(do_nothing) .check_health(check_health) .build() - .expect("Unable to build operation registry") - // Convert it into a router that will route requests to the matching operation - // implementation. - .into(); - - // Setup shared state and middlewares. - let shared_state = Arc::new(State::default()); - let app = app.layer(AddExtensionLayer::new(shared_state)); + .expect("failed to build an instance of PokemonService") + // Setup shared state and middlewares. + .layer(&AddExtensionLayer::new(Arc::new(State::default()))); // Start the [`hyper::Server`]. let bind: SocketAddr = format!("{}:{}", args.address, args.port) diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs index 318dac9091..acaeb55c0c 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs @@ -19,7 +19,6 @@ use pokemon_service_server_sdk::{error, input, model, model::CapturingPayload, o use rand::Rng; use tracing_subscriber::{prelude::*, EnvFilter}; -#[doc(hidden)] pub mod plugin; const PIKACHU_ENGLISH_FLAVOR_TEXT: &str = @@ -58,7 +57,7 @@ struct PokemonTranslations { /// **NOTE: It is up to the implementation of the state structure to handle concurrency by protecting** /// **its attributes using synchronization mechanisms.** /// -/// The framework stores the `Arc` inside an [`http::Extensions`] and conveniently passes it to +/// The framework stores the `Arc` inside an `http::Extensions` and conveniently passes it to /// the operation's implementation, making it able to handle operations with two different async signatures: /// * `FnOnce(InputType) -> Future` /// * `FnOnce(InputType, Extension>) -> Future` diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs index a5a5612a40..8ce0e50d09 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/plugin.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Provides an example [`Plugin`] implementation - [`PrintPlugin`]. + use aws_smithy_http_server::{ operation::{Operation, OperationShape}, plugin::{Plugin, PluginPipeline, PluginStack}, @@ -11,7 +13,7 @@ use tower::{layer::util::Stack, Layer, Service}; use std::task::{Context, Poll}; -/// A [`Service`] that adds a print log. +/// A [`Service`] that prints a given string. #[derive(Clone, Debug)] pub struct PrintService { inner: S, diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 08dbc1c3bf..b2f1ba3191 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -23,6 +23,7 @@ use std::ops::Deref; use thiserror::Error; +#[allow(deprecated)] use crate::request::RequestParts; pub use crate::request::extension::Extension; @@ -35,6 +36,10 @@ pub use crate::request::extension::MissingExtension; /// /// The format given must be the absolute shape ID with `#` replaced with a `.`. #[derive(Debug, Clone)] +#[deprecated( + since = "0.52.0", + note = "This is no longer inserted by the new service builder. Layers should be constructed per operation using the plugin system." +)] pub struct OperationExtension { absolute: &'static str, @@ -50,6 +55,7 @@ pub enum ParseError { MissingNamespace, } +#[allow(deprecated)] impl OperationExtension { /// Creates a new [`OperationExtension`] from the absolute shape ID of the operation with `#` symbol replaced with a `.`. pub fn new(absolute_operation_id: &'static str) -> Result { @@ -123,6 +129,11 @@ impl Deref for RuntimeErrorExtension { /// This is essentially the implementation of `FromRequest` for `Extension`, but with a /// protocol-agnostic rejection type. The actual code-generated implementation simply delegates to /// this function and converts the rejection type into a [`crate::runtime_error::RuntimeError`]. +#[deprecated( + since = "0.52.0", + note = "This was used for extraction under the older service builder. The `FromParts::from_parts` method is now used instead." +)] +#[allow(deprecated)] pub async fn extract_extension( req: &mut RequestParts, ) -> Result, crate::rejection::RequestExtensionNotFoundRejection> @@ -146,6 +157,7 @@ where } #[cfg(test)] +#[allow(deprecated)] mod tests { use super::*; diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 83923bee11..8fefaf242d 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -14,17 +14,13 @@ pub(crate) mod macros; pub mod body; pub(crate) mod error; pub mod extension; -#[doc(hidden)] pub mod instrumentation; -#[doc(hidden)] pub mod operation; -#[doc(hidden)] pub mod plugin; #[doc(hidden)] pub mod protocols; #[doc(hidden)] pub mod rejection; -#[doc(hidden)] pub mod request; #[doc(hidden)] pub mod response; @@ -39,6 +35,7 @@ pub mod routers; pub(crate) use self::error::Error; pub use self::request::extension::Extension; #[doc(inline)] +#[allow(deprecated)] pub use self::routing::Router; #[doc(inline)] pub use tower_http::add_extension::{AddExtension, AddExtensionLayer}; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 0aaec2d50f..3abf9e2540 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -199,7 +199,7 @@ pub struct Operation { } impl Operation { - /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. + /// Applies a [`Layer`](tower::Layer) to the operation _after_ it has been upgraded via [`Operation::upgrade`]. pub fn layer(self, layer: NewL) -> Operation> { Operation { inner: self.inner, diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocols.rs index c3621341a6..b350d2aa77 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocols.rs @@ -5,10 +5,12 @@ //! Protocol helpers. use crate::rejection::MissingContentTypeReason; +#[allow(deprecated)] use crate::request::RequestParts; /// When there are no modeled inputs, /// a request body is empty and the content-type request header must not be set +#[allow(deprecated)] pub fn content_type_header_empty_body_no_modeled_input( req: &RequestParts, ) -> Result<(), MissingContentTypeReason> { @@ -34,6 +36,7 @@ pub fn content_type_header_empty_body_no_modeled_input( } /// Checks that the content-type in request headers is valid +#[allow(deprecated)] pub fn content_type_header_classifier( req: &RequestParts, expected_content_type: Option<&'static str>, @@ -76,6 +79,7 @@ pub fn content_type_header_classifier( Ok(()) } +#[allow(deprecated)] pub fn accept_header_classifier(req: &RequestParts, content_type: &'static str) -> bool { // Allow no ACCEPT header if req.headers().is_none() { @@ -119,6 +123,7 @@ pub fn accept_header_classifier(req: &RequestParts, content_type: &'static }) } +#[allow(deprecated)] #[cfg(test)] mod tests { use super::*; diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index ca780487a6..aef150cd04 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -41,6 +41,10 @@ //! [`crate::runtime_error::RuntimeError`], thus allowing us to represent the full //! error chain. +// For some reason `deprecated(deprecated)` warns of its own deprecation. Putting `allow(deprecated)` at the module +// level remedies it. +#![allow(deprecated)] + use strum_macros::Display; use crate::response::IntoResponse; @@ -49,6 +53,10 @@ use crate::response::IntoResponse; /// extensions]. Contains one variant for each way the extractor can fail. /// /// [request's extensions]: https://docs.rs/http/latest/http/struct.Extensions.html +#[deprecated( + since = "0.52.0", + note = "This was used for extraction under the older service builder. The `MissingExtension` struct returned by `FromParts::from_parts` is now used." +)] #[derive(Debug, Display)] pub enum RequestExtensionNotFoundRejection { /// Used when a particular [`crate::Extension`] was expected to be found in the request but we diff --git a/rust-runtime/aws-smithy-http-server/src/request/mod.rs b/rust-runtime/aws-smithy-http-server/src/request/mod.rs index 7eb6e86baa..22675d7265 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/mod.rs @@ -67,6 +67,10 @@ fn internal_server_error() -> http::Response { } #[doc(hidden)] +#[deprecated( + since = "0.52.0", + note = "This is not used by the new service builder. We use the `http::Parts` struct directly." +)] #[derive(Debug)] pub struct RequestParts { uri: Uri, @@ -75,6 +79,7 @@ pub struct RequestParts { body: Option, } +#[allow(deprecated)] impl RequestParts { /// Create a new `RequestParts`. /// diff --git a/rust-runtime/aws-smithy-http-server/src/response.rs b/rust-runtime/aws-smithy-http-server/src/response.rs index f35c10ed3b..75a5be9759 100644 --- a/rust-runtime/aws-smithy-http-server/src/response.rs +++ b/rust-runtime/aws-smithy-http-server/src/response.rs @@ -34,7 +34,6 @@ use crate::body::BoxBody; -#[doc(hidden)] pub type Response = http::Response; /// A protocol aware function taking `self` to [`http::Response`]. diff --git a/rust-runtime/aws-smithy-http-server/src/routers/mod.rs b/rust-runtime/aws-smithy-http-server/src/routers.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/routers/mod.rs rename to rust-runtime/aws-smithy-http-server/src/routers.rs diff --git a/rust-runtime/aws-smithy-http-server/src/routing/future.rs b/rust-runtime/aws-smithy-http-server/src/routing/future.rs index c6f0657c43..dcb1d83c53 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/future.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/future.rs @@ -32,6 +32,8 @@ * DEALINGS IN THE SOFTWARE. */ +#![allow(deprecated)] + //! Future types. use crate::routers::RoutingFuture; @@ -40,6 +42,10 @@ use super::Route; pub use super::{into_make_service::IntoMakeService, route::RouteFuture}; opaque_future! { + #[deprecated( + since = "0.52.0", + note = "`OperationRegistry` is part of the deprecated service builder API. This type no longer appears in the public API." + )] /// Response future for [`Router`](super::Router). pub type RouterFuture = RoutingFuture, B>; } diff --git a/rust-runtime/aws-smithy-http-server/src/routing/mod.rs b/rust-runtime/aws-smithy-http-server/src/routing/mod.rs index d007f99fcf..2ff9c82ef5 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/mod.rs @@ -44,6 +44,8 @@ pub(crate) mod tiny_map; #[cfg(feature = "aws-lambda")] #[cfg_attr(docsrs, doc(cfg(feature = "aws-lambda")))] pub use self::lambda_handler::LambdaHandler; + +#[allow(deprecated)] pub use self::{ future::RouterFuture, into_make_service::IntoMakeService, @@ -72,6 +74,10 @@ pub use self::{ /// [awsJson1.1]: https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html /// [endpoint trait]: https://awslabs.github.io/smithy/1.0/spec/core/endpoint-traits.html#endpoint-trait #[derive(Debug)] +#[deprecated( + since = "0.52.0", + note = "`OperationRegistry` is part of the deprecated service builder API. This type no longer appears in the public API." +)] pub struct Router { routes: Routes, } @@ -91,6 +97,7 @@ enum Routes { AwsJson1_1(RoutingService>, AwsJson1_1>), } +#[allow(deprecated)] impl Clone for Router { fn clone(&self) -> Self { match &self.routes { @@ -110,6 +117,7 @@ impl Clone for Router { } } +#[allow(deprecated)] impl Router where B: Send + 'static, @@ -259,6 +267,7 @@ where } } +#[allow(deprecated)] impl Service> for Router where B: Send + 'static, @@ -287,6 +296,7 @@ where } #[cfg(test)] +#[allow(deprecated)] mod rest_tests { use super::*; use crate::{ @@ -513,6 +523,7 @@ mod rest_tests { } } +#[allow(deprecated)] #[cfg(test)] mod awsjson_tests { use super::rest_tests::{get_body_as_string, req}; diff --git a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs index 7503af1925..1201ca61aa 100644 --- a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs @@ -174,6 +174,7 @@ impl IntoResponse for RuntimeError { } } +#[allow(deprecated)] impl From for RuntimeError { fn from(err: crate::rejection::RequestExtensionNotFoundRejection) -> Self { Self::InternalFailure(crate::Error::new(err)) diff --git a/rust-runtime/inlineable/src/server_operation_handler_trait.rs b/rust-runtime/inlineable/src/server_operation_handler_trait.rs index 71ad9f84f8..633120912e 100644 --- a/rust-runtime/inlineable/src/server_operation_handler_trait.rs +++ b/rust-runtime/inlineable/src/server_operation_handler_trait.rs @@ -14,12 +14,17 @@ use tower::Service; /// Struct that holds a handler, that is, a function provided by the user that implements the /// Smithy operation. +#[deprecated( + since = "0.52.0", + note = "`OperationHandler` is part of the older service builder API. This type no longer appears in the public API." +)] pub struct OperationHandler { handler: H, #[allow(clippy::type_complexity)] _marker: PhantomData<(B, R, I)>, } +#[allow(deprecated)] impl Clone for OperationHandler where H: Clone, @@ -33,6 +38,11 @@ where } /// Construct an [`OperationHandler`] out of a function implementing the operation. +#[allow(deprecated)] +#[deprecated( + since = "0.52.0", + note = "`OperationHandler` is part of the older service builder API. This type no longer appears in the public API." +)] pub fn operation(handler: H) -> OperationHandler { OperationHandler { handler, @@ -40,6 +50,7 @@ pub fn operation(handler: H) -> OperationHandler { } } +#[allow(deprecated)] impl Service> for OperationHandler where H: Handler, @@ -80,6 +91,10 @@ pub(crate) mod sealed { impl HiddenTrait for Hidden {} } +#[deprecated( + since = "0.52.0", + note = "The inlineable `Handler` is part of the deprecated service builder API. This type no longer appears in the public API." +)] #[async_trait] pub trait Handler: Clone + Send + Sized + 'static { #[doc(hidden)]