-
Notifications
You must be signed in to change notification settings - Fork 190
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
Build services with a derived configuration object #3095
Conversation
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 `Config` object which is provided to the service builder constructor via `PokemonService::builder(config: Config)`. 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 `Config` object, possibly exposing a method to pass in any runtime arguments needed to configure the middleware.
Upgrade guidance discussion: #3096 |
Observations:
|
This comment was marked as outdated.
This comment was marked as outdated.
Actually, I'll make the config builder always fallible to enforce that users register layers -> HTTP plugins -> model plugins in that order. When an injected method to configure pre-applied middleware is used, that method will return a There was also an idea by @hlbarber on how to enforce plugin dependencies at runtime we should follow-up on: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=675785fd01852a155dced99d182f3f02 |
Injected methods may apply a combination of layers and plugins, which makes it tricky to say when in the chain they should appear. I guess the best option is to make them appear as early as possible (e.g. if a method applies a HTTP plugin and a model plugin, it should be invoked after the layers and before the model plugins). |
This comment was marked as outdated.
This comment was marked as outdated.
A new generated diff is ready to view.
A new doc preview is ready to view. |
@@ -73,9 +73,9 @@ open class ServerRootGenerator( | |||
//! ```rust,no_run | |||
//! ## use std::net::SocketAddr; | |||
//! ## async fn dummy() { | |||
//! use $crateName::$serviceName; | |||
//! use $crateName::{${serviceName}Config, $serviceName}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit but shouldn't $serviceName
go first?
} | ||
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces
A new generated diff is ready to view.
A new doc preview is ready to view. |
PR #3095 added a code-generated `${serviceName}Config` object on which users can register layers and plugins. For example: ```rust let config = PokemonServiceConfig::builder() .layer(layers) .http_plugin(authn_plugin) .model_plugin(authz_plugin) .build(); ``` This PR makes it so that server decorators can inject methods on this config builder object. These methods can apply arbitrary layers, HTTP plugins, and/or model plugins. Moreover, the decorator can configure whether invoking such method is required or not. For example, a decorator can inject an `aws_auth` method that configures some plugins using its input arguments. Missing invocation of this method will result in the config failing to build: ```rust let _: SimpleServiceConfig< // No layers have been applied. tower::layer::util::Identity, // One HTTP plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, // One model plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, > = SimpleServiceConfig::builder() // This method has been injected in the config builder! .aws_auth("a".repeat(69).to_owned(), 69) // The method will apply one HTTP plugin and one model plugin, // configuring them with the input arguments. Configuration can be // declared to be fallible, in which case we get a `Result` we unwrap // here. .expect("failed to configure aws_auth") .build() // Since `aws_auth` has been marked as required, if the user misses // invoking it, this would panic here. .unwrap(); ```
PR #3095 added a code-generated `${serviceName}Config` object on which users can register layers and plugins. For example: ```rust let config = PokemonServiceConfig::builder() .layer(layers) .http_plugin(authn_plugin) .model_plugin(authz_plugin) .build(); ``` This PR makes it so that server decorators can inject methods on this config builder object. These methods can apply arbitrary layers, HTTP plugins, and/or model plugins. Moreover, the decorator can configure whether invoking such method is required or not. For example, a decorator can inject an `aws_auth` method that configures some plugins using its input arguments. Missing invocation of this method will result in the config failing to build: ```rust let _: SimpleServiceConfig< // No layers have been applied. tower::layer::util::Identity, // One HTTP plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, // One model plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, > = SimpleServiceConfig::builder() // This method has been injected in the config builder! .aws_auth("a".repeat(69).to_owned(), 69) // The method will apply one HTTP plugin and one model plugin, // configuring them with the input arguments. Configuration can be // declared to be fallible, in which case we get a `Result` we unwrap // here. .expect("failed to configure aws_auth") .build() // Since `aws_auth` has been marked as required, if the user misses // invoking it, this would panic here. .unwrap(); ``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
PR #3095 added a code-generated `${serviceName}Config` object on which users can register layers and plugins. For example: ```rust let config = PokemonServiceConfig::builder() .layer(layers) .http_plugin(authn_plugin) .model_plugin(authz_plugin) .build(); ``` This PR makes it so that server decorators can inject methods on this config builder object. These methods can apply arbitrary layers, HTTP plugins, and/or model plugins. Moreover, the decorator can configure whether invoking such method is required or not. For example, a decorator can inject an `aws_auth` method that configures some plugins using its input arguments. Missing invocation of this method will result in the config failing to build: ```rust let _: SimpleServiceConfig< // No layers have been applied. tower::layer::util::Identity, // One HTTP plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, // One model plugin has been applied. PluginStack<IdentityPlugin, IdentityPlugin>, > = SimpleServiceConfig::builder() // This method has been injected in the config builder! .aws_auth("a".repeat(69).to_owned(), 69) // The method will apply one HTTP plugin and one model plugin, // configuring them with the input arguments. Configuration can be // declared to be fallible, in which case we get a `Result` we unwrap // here. .expect("failed to configure aws_auth") .build() // Since `aws_auth` has been marked as required, if the user misses // invoking it, this would panic here. .unwrap(); ``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
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 isprovided to the service builder constructor via
PokemonService::builder(config: PokemonServiceConfig)
. This will displace thecurrent
builder_without_plugins
andbuilder_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 asmithy-rs decorator that pre-applied an authorization plugin inside the
${serviceName}Config
object, possibly exposing a method to pass in anyruntime arguments needed to configure the middleware.
Checklist
CHANGELOG.next.toml
if I made changes to the smithy-rs codegen or runtime cratesBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.