Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhide new service builder and deprecate the prior #1886

Merged
merged 67 commits into from
Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
40bc255
Update examples to use new service builder
Oct 11, 2022
1e6a31f
Unhide the example plugin
Oct 11, 2022
26dcfbf
Deprecate OperationRegistry[Builder]
Oct 11, 2022
48673da
Unhide "The Anatomy of a Service"
Oct 11, 2022
81150f5
Update instrumentation.md documentation
Oct 11, 2022
4dcb0ba
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Oct 25, 2022
e7dec39
Uncomment middleware documentation
Oct 25, 2022
d7d78c1
Add more deprecation
Oct 26, 2022
eed7efd
Unhide modules
Oct 26, 2022
bd7b594
Rename routers/mod.rs to routers.rs
Oct 26, 2022
c54aca7
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Oct 31, 2022
2147e1d
Fix deprecated semver
Nov 1, 2022
1fe4d06
Allow in server_operation_handler_trait.rs
Nov 1, 2022
a863887
Allow deprecated on Router tests
Nov 1, 2022
d01aec2
Unhide request and response
Nov 1, 2022
2da3b39
Change pokemon_service.md
Nov 1, 2022
6bdd2d0
Permute overview.md
Nov 1, 2022
51d2b60
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 16, 2022
d63d02f
Deprecate OperationExtension
Nov 16, 2022
1447654
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 16, 2022
d69e7d9
Update instrumentation.md
Nov 16, 2022
c03ce5d
Allow deprecated on tests
Nov 16, 2022
82b1aeb
Update examples to use new-er service builder
Nov 16, 2022
fedb05c
Add docs
Nov 16, 2022
bd78358
Deprecate RequestParts
Nov 16, 2022
8d5cf82
Cleanup
Nov 16, 2022
95790c5
Unhide operation_shape and service
Nov 17, 2022
f16fd04
Re-hide entire response module
Nov 18, 2022
fe09eca
Deprecate RouterFuture
Nov 18, 2022
452f5f5
Add deprecated attribute
Nov 18, 2022
84c287a
Deprecate `operation_registry` module
Nov 18, 2022
6e67847
Add link to re-exported builder
Nov 18, 2022
76809d3
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 21, 2022
93c2c34
Unhide service at root
Nov 21, 2022
6e55abb
Escape the ## Deprecation
Nov 21, 2022
f365bbb
Fix doc links
Nov 21, 2022
793fe5c
Fix since on operation_registry
Nov 21, 2022
7d1d93b
Remove unused exports
Nov 21, 2022
e4e7739
Add CHANGELOG.next.toml
Nov 21, 2022
d9bf579
Fix integration tests
Nov 21, 2022
83da872
Fix CHANGELOG.next.toml
Nov 21, 2022
4bf54d8
Use root level service export
Nov 21, 2022
7c4bf48
Improve documentation
Nov 21, 2022
3426dbe
Use ConnectInfo rather than Extension<ConnectInfo>
Nov 21, 2022
5db882d
Update CHANGELOG.next.toml
Nov 21, 2022
fb73260
Remove unused import
Nov 21, 2022
1ed7513
Fix ConnectInfo unwrapping
Nov 21, 2022
2389026
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 22, 2022
5601be8
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 22, 2022
3f73966
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 29, 2022
b23265a
Use LeafModule data class
Nov 29, 2022
b596f2a
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Nov 30, 2022
c4f8f68
Apply suggestions from code review
hlbarber Nov 30, 2022
1ba085f
Update CHANGELOG.next.toml
hlbarber Nov 30, 2022
9393d40
Add Context to get_storage_lambda
Dec 1, 2022
17959e8
Compactify get_storage_lambda
Dec 1, 2022
cbd2aeb
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Dec 1, 2022
533dc24
Merge branch 'main' into harryb/builder-unhide-and-deprecate
hlbarber Dec 1, 2022
69da184
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Dec 2, 2022
152f716
Switch service to private
Dec 2, 2022
7d113f7
Merge branch 'main' into harryb/builder-unhide-and-deprecate
Dec 2, 2022
2a2415e
Re-add documentation
Dec 2, 2022
62f278c
Fix hyper and Service link
Dec 2, 2022
ddef4c6
Change headers to H3
Dec 2, 2022
234bc72
Update CHANGELOG.next.toml
Dec 2, 2022
d510b50
Improve CHANGELOG.next.toml wrapping
Dec 2, 2022
eddd06f
Fix CHANGELOG.next.toml typo
Dec 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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>" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
//!
Expand Down Expand Up @@ -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(),
)
}

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

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

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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<
Expand Down
10 changes: 6 additions & 4 deletions design/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<!-- - [The Anatomy of a Service](./server/anatomy.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)
Expand Down
14 changes: 11 additions & 3 deletions design/src/server/instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- TODO: Link to it when the logging module is no longer `#[doc(hidden)]` -->
```rust
use aws_smithy_http_server::instrumentation::InstrumentExt;

<!-- TODO: Document use of the `InstrumentExt` after the new service builder is released. -->
let plugins = PluginPipeline::new().instrument();
let app = PokemonService::builder_with_plugins(plugins)
.get_pokemon_species(/* handler */)
/* ... */
.build();
```

<!-- TODO: Link to it when the logging module is no longer `#[doc(hidden)]` -->

### Example

Expand Down
8 changes: 4 additions & 4 deletions design/src/server/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<!-- - [Middleware](./middleware.md) -->
- [Instrumentation](./instrumentation.md)
<!-- - [The Anatomy of a Service](./anatomy.md) -->
<!-- - [Accessing Un-modelled Data](./from_parts.md) -->
Original file line number Diff line number Diff line change
Expand Up @@ -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::<std::net::SocketAddr>());
let server = hyper::Server::bind(&bind).serve(app.into_make_service_with_connect_info::<SocketAddr>());

// Run forever-ish...
if let Err(err) = server.await {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arc<State>>,
context: Context,
) -> Result<output::GetStorageOutput, error::GetStorageError> {
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);
Expand Down
Loading