Skip to content

Commit 5eb0927

Browse files
82marbaghlbarber
andauthored
OperationShape::NAME as ShapeId (#2678) (#2717)
See #2634 ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Signed-off-by: Daniele Ahmed <ahmeddan@amazon.de> Co-authored-by: Harry Barber <106155934+hlbarber@users.noreply.github.com>
1 parent dabbfaa commit 5eb0927

File tree

17 files changed

+205
-125
lines changed

17 files changed

+205
-125
lines changed

CHANGELOG.next.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,32 @@ message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurren
133133
references = ["smithy-rs#2742"]
134134
meta = { "breaking" = true, "tada" = false, "bug" = false }
135135
author = "ysaito1001"
136+
137+
[[smithy-rs]]
138+
message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name.
139+
`OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`.
140+
141+
Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation:
142+
143+
```
144+
filter_by_operation_name(plugin, |name| name != Op::NAME);
145+
```
146+
147+
Your new filter selects on an operation's absolute name, namespace or name.
148+
149+
```
150+
filter_by_operation_id(plugin, |id| id.name() != Op::NAME.name());
151+
```
152+
153+
The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model.
154+
155+
You can filter all operations in a namespace or absolute name:
156+
157+
```
158+
filter_by_operation_id(plugin, |id| id.namespace() != "namespace");
159+
filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name");
160+
```
161+
"""
162+
author = "82marbag"
163+
references = ["smithy-rs#2678"]
164+
meta = { "breaking" = true, "tada" = false, "bug" = false }

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust
1313
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
1414
import software.amazon.smithy.rust.codegen.core.rustlang.writable
1515
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
16+
import software.amazon.smithy.rust.codegen.core.util.dq
1617
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
1718
import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
1819

@@ -49,12 +50,13 @@ class ServerOperationGenerator(
4950
val requestFmt = generator.requestFmt()
5051
val responseFmt = generator.responseFmt()
5152

53+
val operationIdAbsolute = operationId.toString().replace("#", "##")
5254
writer.rustTemplate(
5355
"""
5456
pub struct $operationName;
5557
5658
impl #{SmithyHttpServer}::operation::OperationShape for $operationName {
57-
const NAME: &'static str = "${operationId.toString().replace("#", "##")}";
59+
const NAME: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()});
5860
5961
type Input = crate::input::${operationName}Input;
6062
type Output = crate::output::${operationName}Output;

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,13 +478,13 @@ class ServerServiceGenerator(
478478
}
479479

480480
private fun missingOperationsError(): Writable = writable {
481-
rust(
481+
rustTemplate(
482482
"""
483483
/// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not
484484
/// specified.
485485
##[derive(Debug)]
486486
pub struct MissingOperationsError {
487-
operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>,
487+
operation_names2setter_methods: std::collections::HashMap<#{SmithyHttpServer}::shape_id::ShapeId, &'static str>,
488488
}
489489
490490
impl std::fmt::Display for MissingOperationsError {
@@ -495,7 +495,7 @@ class ServerServiceGenerator(
495495
We are missing handlers for the following operations:\n",
496496
)?;
497497
for operation_name in self.operation_names2setter_methods.keys() {
498-
writeln!(f, "- {}", operation_name)?;
498+
writeln!(f, "- {}", operation_name.absolute())?;
499499
}
500500
501501
writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?;
@@ -508,6 +508,7 @@ class ServerServiceGenerator(
508508
509509
impl std::error::Error for MissingOperationsError {}
510510
""",
511+
*codegenScope,
511512
)
512513
}
513514

examples/pokemon-service/src/plugin.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use aws_smithy_http_server::{
99
operation::{Operation, OperationShape},
1010
plugin::{Plugin, PluginPipeline, PluginStack},
11+
shape_id::ShapeId,
1112
};
1213
use tower::{layer::util::Stack, Layer, Service};
1314

@@ -17,7 +18,7 @@ use std::task::{Context, Poll};
1718
#[derive(Clone, Debug)]
1819
pub struct PrintService<S> {
1920
inner: S,
20-
name: &'static str,
21+
id: ShapeId,
2122
}
2223

2324
impl<R, S> Service<R> for PrintService<S>
@@ -33,23 +34,23 @@ where
3334
}
3435

3536
fn call(&mut self, req: R) -> Self::Future {
36-
println!("Hi {}", self.name);
37+
println!("Hi {}", self.id.absolute());
3738
self.inner.call(req)
3839
}
3940
}
4041

4142
/// A [`Layer`] which constructs the [`PrintService`].
4243
#[derive(Debug)]
4344
pub struct PrintLayer {
44-
name: &'static str,
45+
id: ShapeId,
4546
}
4647
impl<S> Layer<S> for PrintLayer {
4748
type Service = PrintService<S>;
4849

4950
fn layer(&self, service: S) -> Self::Service {
5051
PrintService {
5152
inner: service,
52-
name: self.name,
53+
id: self.id.clone(),
5354
}
5455
}
5556
}
@@ -66,7 +67,7 @@ where
6667
type Layer = Stack<L, PrintLayer>;
6768

6869
fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
69-
input.layer(PrintLayer { name: Op::NAME })
70+
input.layer(PrintLayer { id: Op::NAME })
7071
}
7172
}
7273

rust-runtime/aws-smithy-http-server/src/extension.rs

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,27 @@
1919
//!
2020
//! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
2121
22-
use std::{fmt, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll};
22+
use std::hash::Hash;
23+
use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll};
2324

25+
use crate::extension;
2426
use futures_util::ready;
2527
use futures_util::TryFuture;
2628
use thiserror::Error;
2729
use tower::{layer::util::Stack, Layer, Service};
2830

2931
use crate::operation::{Operation, OperationShape};
30-
use crate::plugin::{plugin_from_operation_name_fn, OperationNameFn, Plugin, PluginPipeline, PluginStack};
32+
use crate::plugin::{plugin_from_operation_id_fn, OperationIdFn, Plugin, PluginPipeline, PluginStack};
33+
use crate::shape_id::ShapeId;
3134

3235
pub use crate::request::extension::{Extension, MissingExtension};
3336

3437
/// Extension type used to store information about Smithy operations in HTTP responses.
3538
/// This extension type is inserted, via the [`OperationExtensionPlugin`], whenever it has been correctly determined
3639
/// that the request should be routed to a particular operation. The operation handler might not even get invoked
3740
/// because the request fails to deserialize into the modeled operation input.
38-
#[derive(Debug, Clone, PartialEq, Eq)]
39-
pub struct OperationExtension {
40-
absolute: &'static str,
41-
42-
namespace: &'static str,
43-
name: &'static str,
44-
}
41+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42+
pub struct OperationExtension(pub ShapeId);
4543

4644
/// An error occurred when parsing an absolute operation shape ID.
4745
#[derive(Debug, Clone, Error, PartialEq, Eq)]
@@ -51,36 +49,6 @@ pub enum ParseError {
5149
MissingNamespace,
5250
}
5351

54-
#[allow(deprecated)]
55-
impl OperationExtension {
56-
/// Creates a new [`OperationExtension`] from the absolute shape ID.
57-
pub fn new(absolute_operation_id: &'static str) -> Result<Self, ParseError> {
58-
let (namespace, name) = absolute_operation_id
59-
.rsplit_once('#')
60-
.ok_or(ParseError::MissingNamespace)?;
61-
Ok(Self {
62-
absolute: absolute_operation_id,
63-
namespace,
64-
name,
65-
})
66-
}
67-
68-
/// Returns the Smithy model namespace.
69-
pub fn namespace(&self) -> &'static str {
70-
self.namespace
71-
}
72-
73-
/// Returns the Smithy operation name.
74-
pub fn name(&self) -> &'static str {
75-
self.name
76-
}
77-
78-
/// Returns the absolute operation shape ID.
79-
pub fn absolute(&self) -> &'static str {
80-
self.absolute
81-
}
82-
}
83-
8452
pin_project_lite::pin_project! {
8553
/// The [`Service::Future`] of [`OperationExtensionService`] - inserts an [`OperationExtension`] into the
8654
/// [`http::Response]`.
@@ -154,7 +122,7 @@ impl<S> Layer<S> for OperationExtensionLayer {
154122
}
155123

156124
/// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation.
157-
pub struct OperationExtensionPlugin(OperationNameFn<fn(&'static str) -> OperationExtensionLayer>);
125+
pub struct OperationExtensionPlugin(OperationIdFn<fn(ShapeId) -> OperationExtensionLayer>);
158126

159127
impl fmt::Debug for OperationExtensionPlugin {
160128
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -170,7 +138,7 @@ where
170138
type Layer = Stack<L, OperationExtensionLayer>;
171139

172140
fn map(&self, input: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
173-
<OperationNameFn<fn(&'static str) -> OperationExtensionLayer> as Plugin<P, Op, S, L>>::map(&self.0, input)
141+
<OperationIdFn<fn(ShapeId) -> OperationExtensionLayer> as Plugin<P, Op, S, L>>::map(&self.0, input)
174142
}
175143
}
176144

@@ -184,9 +152,8 @@ pub trait OperationExtensionExt<P> {
184152

185153
impl<P> OperationExtensionExt<P> for PluginPipeline<P> {
186154
fn insert_operation_extension(self) -> PluginPipeline<PluginStack<OperationExtensionPlugin, P>> {
187-
let plugin = OperationExtensionPlugin(plugin_from_operation_name_fn(|name| {
188-
let operation_extension = OperationExtension::new(name).expect("Operation name is malformed, this should never happen. Please file an issue against https://github.com/awslabs/smithy-rs");
189-
OperationExtensionLayer(operation_extension)
155+
let plugin = OperationExtensionPlugin(plugin_from_operation_id_fn(|shape_id| {
156+
OperationExtensionLayer(extension::OperationExtension(shape_id))
190157
}));
191158
self.push(plugin)
192159
}
@@ -243,28 +210,27 @@ mod tests {
243210
#[test]
244211
fn ext_accept() {
245212
let value = "com.amazonaws.ebs#CompleteSnapshot";
246-
let ext = OperationExtension::new(value).unwrap();
213+
let ext = ShapeId::new(
214+
"com.amazonaws.ebs#CompleteSnapshot",
215+
"com.amazonaws.ebs",
216+
"CompleteSnapshot",
217+
);
247218

248219
assert_eq!(ext.absolute(), value);
249220
assert_eq!(ext.namespace(), "com.amazonaws.ebs");
250221
assert_eq!(ext.name(), "CompleteSnapshot");
251222
}
252223

253-
#[test]
254-
fn ext_reject() {
255-
let value = "CompleteSnapshot";
256-
assert_eq!(
257-
OperationExtension::new(value).unwrap_err(),
258-
ParseError::MissingNamespace
259-
)
260-
}
261-
262224
#[tokio::test]
263225
async fn plugin() {
264226
struct DummyOp;
265227

266228
impl OperationShape for DummyOp {
267-
const NAME: &'static str = "com.amazonaws.ebs#CompleteSnapshot";
229+
const NAME: ShapeId = ShapeId::new(
230+
"com.amazonaws.ebs#CompleteSnapshot",
231+
"com.amazonaws.ebs",
232+
"CompleteSnapshot",
233+
);
268234

269235
type Input = ();
270236
type Output = ();
@@ -283,8 +249,8 @@ mod tests {
283249

284250
// Check for `OperationExtension`.
285251
let response = svc.oneshot(http::Request::new(())).await.unwrap();
286-
let expected = OperationExtension::new(DummyOp::NAME).unwrap();
252+
let expected = DummyOp::NAME;
287253
let actual = response.extensions().get::<OperationExtension>().unwrap();
288-
assert_eq!(*actual, expected);
254+
assert_eq!(actual.0, expected);
289255
}
290256
}

rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@
55

66
use tower::Layer;
77

8+
use crate::shape_id::ShapeId;
9+
810
use super::{InstrumentOperation, MakeIdentity};
911

1012
/// A [`Layer`] used to apply [`InstrumentOperation`].
1113
#[derive(Debug)]
1214
pub struct InstrumentLayer<RequestMakeFmt = MakeIdentity, ResponseMakeFmt = MakeIdentity> {
13-
operation_name: &'static str,
15+
operation_id: ShapeId,
1416
make_request: RequestMakeFmt,
1517
make_response: ResponseMakeFmt,
1618
}
1719

1820
impl InstrumentLayer {
1921
/// Constructs a new [`InstrumentLayer`] with no data redacted.
20-
pub fn new(operation_name: &'static str) -> Self {
22+
pub fn new(operation_id: ShapeId) -> Self {
2123
Self {
22-
operation_name,
24+
operation_id,
2325
make_request: MakeIdentity,
2426
make_response: MakeIdentity,
2527
}
@@ -32,7 +34,7 @@ impl<RequestMakeFmt, ResponseMakeFmt> InstrumentLayer<RequestMakeFmt, ResponseMa
3234
/// The argument is typically [`RequestFmt`](super::sensitivity::RequestFmt).
3335
pub fn request_fmt<R>(self, make_request: R) -> InstrumentLayer<R, ResponseMakeFmt> {
3436
InstrumentLayer {
35-
operation_name: self.operation_name,
37+
operation_id: self.operation_id,
3638
make_request,
3739
make_response: self.make_response,
3840
}
@@ -43,7 +45,7 @@ impl<RequestMakeFmt, ResponseMakeFmt> InstrumentLayer<RequestMakeFmt, ResponseMa
4345
/// The argument is typically [`ResponseFmt`](super::sensitivity::ResponseFmt).
4446
pub fn response_fmt<R>(self, make_response: R) -> InstrumentLayer<RequestMakeFmt, R> {
4547
InstrumentLayer {
46-
operation_name: self.operation_name,
48+
operation_id: self.operation_id,
4749
make_request: self.make_request,
4850
make_response,
4951
}
@@ -58,7 +60,7 @@ where
5860
type Service = InstrumentOperation<S, RequestMakeFmt, ResponseMakeFmt>;
5961

6062
fn layer(&self, service: S) -> Self::Service {
61-
InstrumentOperation::new(service, self.operation_name)
63+
InstrumentOperation::new(service, self.operation_id.clone())
6264
.request_fmt(self.make_request.clone())
6365
.response_fmt(self.make_response.clone())
6466
}

rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
//! ```
1414
//! # use std::convert::Infallible;
1515
//! # use aws_smithy_http_server::instrumentation::{*, sensitivity::{*, headers::*, uri::*}};
16+
//! # use aws_smithy_http_server::shape_id::ShapeId;
1617
//! # use http::{Request, Response};
1718
//! # use tower::{util::service_fn, Service};
1819
//! # async fn service(request: Request<()>) -> Result<Response<()>, Infallible> {
1920
//! # Ok(Response::new(()))
2021
//! # }
2122
//! # async fn example() {
2223
//! # let service = service_fn(service);
24+
//! # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation");
2325
//! let request = Request::get("http://localhost/a/b/c/d?bar=hidden")
2426
//! .header("header-name-a", "hidden")
2527
//! .body(())
@@ -47,7 +49,7 @@
4749
//! }
4850
//! })
4951
//! .status_code();
50-
//! let mut service = InstrumentOperation::new(service, "foo-operation")
52+
//! let mut service = InstrumentOperation::new(service, NAME)
5153
//! .request_fmt(request_fmt)
5254
//! .response_fmt(response_fmt);
5355
//!

rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ where
2626
type Layer = Stack<L, InstrumentLayer<Op::RequestFmt, Op::ResponseFmt>>;
2727

2828
fn map(&self, operation: Operation<S, L>) -> Operation<Self::Service, Self::Layer> {
29-
let layer = InstrumentLayer::new(Op::NAME)
29+
let operation_id = Op::NAME;
30+
let layer = InstrumentLayer::new(operation_id)
3031
.request_fmt(Op::request_fmt())
3132
.response_fmt(Op::response_fmt());
3233
operation.layer(layer)

0 commit comments

Comments
 (0)