You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parameterize Plugin by service rather than protocol (#2772)
## Motivation and Context
Closes#1839
Currently, `Plugin` is parameterized by protocol and operation. To
improve symmetry, extensibility and uniformity we switch this to be
parameterized by service instead. The protocol can still be recovered
via the `type Protocol` associated type on `ServiceShape`.
## Description
- Add `ServiceShape` trait, encoding the properties of a Smithy service.
- Change `Plugin<Protocol, Operation, S>` to `Plugin<Service, Operation,
S>`.
- Add `FilterByOperation` and `filter_by_operation` `Plugin`s.
The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal:
185
+
message = """The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal:
187
186
187
+
- A `ServiceShape` trait has been added.
188
188
- The `Plugin` trait has been simplified.
189
189
- The `Operation` structure has been removed.
190
190
- A `Scoped` `Plugin` has been added.
191
191
192
192
The `Plugin` trait has now been simplified and the `Operation` struct has been removed.
193
193
194
+
## Addition of `ServiceShape`
195
+
196
+
Since the [0.52 release](https://github.com/awslabs/smithy-rs/releases/tag/release-2022-12-12) the `OperationShape` has existed.
/// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error
211
+
/// exists.
212
+
type Error;
213
+
}
214
+
```
215
+
216
+
This allowed `Plugin` authors to access these associated types and constants. See the [`PrintPlugin`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs) as an example.
217
+
218
+
We continue with this approach and introduce the following trait:
219
+
220
+
```rust
221
+
/// Models the [Smithy Service shape].
222
+
///
223
+
/// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html
/// An enumeration of all operations contained in this service.
237
+
type Operations;
238
+
}
239
+
```
240
+
241
+
With the changes to `Plugin`, described below, middleware authors now have access to this information at compile time.
242
+
194
243
## Simplication of the `Plugin` trait
195
244
196
245
Previously,
@@ -209,14 +258,16 @@ modified an `Operation`.
209
258
Now,
210
259
211
260
```rust
212
-
trait Plugin<Protocol, Operation, S> {
213
-
type Service;
261
+
trait Plugin<Service, Operation, T> {
262
+
type Output;
214
263
215
-
fn apply(&self, svc: S) -> Self::Service;
264
+
fn apply(&self, input: T) -> Self::Output;
216
265
}
217
266
```
218
267
219
-
maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Protocol` and `Operation`.
268
+
maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Service` and `Operation`, which implement `ServiceShape` and `OperationShape` respectively.
269
+
270
+
Having both `Service` and `Operation` as type parameters also provides an even surface for advanced users to extend the codegenerator in a structured way. See [this issue](https://github.com/awslabs/smithy-rs/issues/2777) for more context.
220
271
221
272
The following middleware setup
222
273
@@ -286,18 +337,33 @@ where
286
337
287
338
pub struct PrintPlugin;
288
339
289
-
impl<P, Op, S, L> Plugin<P, Op, S, L> for PrintPlugin
340
+
impl<Service, Op, T> Plugin<Service, Operation, T> for PrintPlugin
290
341
where
291
342
Op: OperationShape,
292
343
{
293
-
type Service = PrintService<S>;
344
+
type Output = PrintService<S>;
294
345
295
-
fn apply(&self, svc: S) -> Self::Service {
346
+
fn apply(&self, inner: T) -> Self::Output {
296
347
PrintService { inner, name: Op::ID.name() }
297
348
}
298
349
}
299
350
```
300
351
352
+
Alternatively, using the new `ServiceShape`, implemented on `Ser`:
353
+
354
+
```rust
355
+
impl<Service, Operation, T> Plugin<Service, Operation, T> for PrintPlugin
356
+
where
357
+
Ser: ServiceShape,
358
+
{
359
+
type Service = PrintService<S>;
360
+
361
+
fn apply(&self, inner: T) -> Self::Service {
362
+
PrintService { inner, name: Ser::ID.name() }
363
+
}
364
+
}
365
+
```
366
+
301
367
A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately:
302
368
303
369
```rust
@@ -442,3 +508,45 @@ message = "Add a `send_with` function on `-Input` types for sending requests wit
message = """Remove `filter_by_operation_id` and `plugin_from_operation_id_fn` in favour of `filter_by_operation` and `plugin_from_operation_fn`.
514
+
515
+
Previously, we provided `filter_by_operation_id` which filtered `Plugin` application via a predicate over the Shape ID.
516
+
517
+
```rust
518
+
use aws_smithy_http_server::plugin::filter_by_operation_id;
519
+
use pokemon_service_server_sdk::operation_shape::CheckHealth;
520
+
521
+
let filtered = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME);
522
+
```
523
+
524
+
This had the problem that the user is unable to exhaustively match over a `&'static str`. To remedy this we have switched to `filter_by_operation` which is a predicate over an enum containing all operations contained in the service.
525
+
526
+
```rust
527
+
use aws_smithy_http_server::plugin::filter_by_operation_id;
528
+
use pokemon_service_server_sdk::service::Operation;
529
+
530
+
let filtered = filter_by_operation(plugin, |op: Operation| op != Operation::CheckHealth);
531
+
```
532
+
533
+
Similarly, `plugin_from_operation_fn` now allows for
534
+
535
+
```rust
536
+
use aws_smithy_http_server::plugin::plugin_from_operation_fn;
537
+
use pokemon_service_server_sdk::service::Operation;
Copy file name to clipboardExpand all lines: codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -108,7 +108,7 @@ class ScopeMacroGenerator(
108
108
/// ## use #{SmithyHttpServer}::plugin::{Plugin, Scoped};
109
109
/// ## use $crateName::scope;
110
110
/// ## struct MockPlugin;
111
-
/// ## impl<P, Op, S> Plugin<P, Op, S> for MockPlugin { type Service = u32; fn apply(&self, svc: S) -> u32 { 3 } }
Copy file name to clipboardExpand all lines: codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt
0 commit comments