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

Add server SDK constraint traits RFC #1199

Merged
merged 8 commits into from
Dec 1, 2022
Merged

Conversation

david-perez
Copy link
Contributor

@david-perez david-perez commented Feb 17, 2022

Rendered.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@david-perez david-perez requested a review from a team as a code owner February 17, 2022 12:53
@david-perez david-perez added the server Rust server SDK label Feb 17, 2022
@github-actions
Copy link

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Server Test

than 0.
2. In request deserialization, should we fail with the first violation and
immediately render a response, or attempt to parse the entire request and
provide a complete and structured report?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of security I think we should bail out as soon as we find the first validation error.

Copy link

@adamthom-amzn adamthom-amzn Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the opposite opinion. Validation doesn't protect the parser (you've already parsed), and it's extremely useful to applications that are gathering user input, such as web forms, to indicate all invalid inputs so that they can be fixed by the user at once. This is why the ValidationException built into Smithy has the structure that it does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend having a customizeable max-limit on the number of validation errors that can be encountered before an early return, with the option to set it to None or something for no limit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the doc Adam, i understand the reasoning and I agree with you and Jon..

Comment on lines 415 to 416
* It becomes _possible_ to send responses with invalid operation outputs. All
the service framework could do is log the validation errors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really care about this? At the end the server is the authority and we do not validate outputs..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least I'd consider it to likely be an implementation error from the user with regards to what they modeled, and as such performing validation and logging warnings would help them identify it. So we should care, but very little.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a service owner you probably do care that your outputs do not match your model: your server is breaking the contract you want to set with your clients.
So being able to have clear metrics about it seems important. Consistent logging would allow that.

Comment on lines 138 to 140
that violates the model. The actual check is performed in the implementation of
[`TryFrom`]`<InnerType>` for the generated struct, which makes it convenient to use
the [`?` operator for error propagation]. Each constrained struct will have a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is clever, I like it!

implement it by wrapping the incoming value in the aforementioned tuple struct
to perform the validation, and immediately unwrap it.

Comparative advantages:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the first option follows a better approach in terms of design, but I would be fine also doing 2. if we find problems with 1. Both options solve the problem in a good way :)


fn try_from(value: String) -> Result<Self, Self::Error> {
let num_code_points = value.chars().count();
if 1 <= num_code_points && num_code_points <= 69 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using RangeInclusive::contains here

}
```

(Note that we're using the _linear_ time check `chars().count()` instead of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Unicode is hard.

Copy link
Contributor

@jonhoo jonhoo Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that chars().count() actually gives you what you want. Specifically, I'm not actually positive that char == code point. From memory, char is actually a "Unicode Scalar Value", which is subtly different from a Unicode code point. See also burntsushi's excellent write-up here: BurntSushi/aho-corasick#72 (comment). Although it could be that you're right, an .chars() actually iterates over code points. But worth double-checking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unicode scalar values are Unicode Code Points minus the "surrogate pairs range" from D800 to DFFF which is only used in UTF-16. Since in Rust strings are UTF-8 encoded we can't count those. See also smithy-lang/smithy#1089

pub enum NiceStringValidationError {
/// Validation error holding the number of Unicode code points found, when a value between `1` and
/// `69` (inclusive) was expected.
LengthViolation(usize),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be too much information, but I always prefer errors that explain not only what the invalid value was, but what counts as a valid value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I see in the next paragraph that you say you'll do this.

> restricts string shape values to a specified regular expression.

We will implement this by using the `regex`'s crate [`is_match`]. We will use
[`lazy_static!`] to compile the regex only the first time it is required.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once_cell is now preferred to lazy_static

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Came here to say exactly this 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the link in the RFC text is broken.

1. Should we code-generate unsigned integer types (`u16`, `u32`, `u64`) when
the `range` trait is applied with `min` set to a value greater than or equal
to 0?
- A user has even suggested to use the `std::num::NonZeroUX` types (e.g.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I'm all for using the num crate in situations like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What specifically would you use from the num crate?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My experience has been that the num crate introduces more problems than it solves. I don't think it gives us functionality that'd be super relevant here.

}
```

(Note that we're using the _linear_ time check `chars().count()` instead of
Copy link
Contributor

@jonhoo jonhoo Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that chars().count() actually gives you what you want. Specifically, I'm not actually positive that char == code point. From memory, char is actually a "Unicode Scalar Value", which is subtly different from a Unicode code point. See also burntsushi's excellent write-up here: BurntSushi/aho-corasick#72 (comment). Although it could be that you're right, an .chars() actually iterates over code points. But worth double-checking.


where:

* <StructName> is the name of the generated struct,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't render right. The <> bits need to be in backtics, otherwise they're treated as custom HTML elements.

pub enum NiceStringValidationError {
/// Validation error holding the number of Unicode code points found, when a value between `1` and
/// `69` (inclusive) was expected.
LengthViolation(usize),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name here may need to be more complex if it's possible to have multiple constraints of the same type. For example:

@length(min: 1)
@length(max: 69)

This would (presumably) make two variants, one for each constraint, and both of them are length constraints. This may also be helpful given, as you say later, that we may want different templates for different configurations of a given constraint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see it documented in the spec so I tested it; Smithy does not allow you to specify the same constraint more than once on the same shape.

ERROR: com.amazonaws.simple#HeaderBoundData (Model)
     @ /local/home/davidpz/workplace/smithy-ws/src/SmithyRsSource/codegen-server-test/model/simple.smithy
     |
  29 | @length(min: 1)
     |         ^
     = Conflicting `smithy.api#length` trait found on shape `com.amazonaws.simple#HeaderBoundData`. The previous trait was defined at `/local/home/davidpz/workplace/smithy-ws/src/SmithyRsSource/codegen-server-test/model/simple.smithy [28, 9]`, and a conflicting trait was defined at `/local/home/davidpz/workplace/smithy-ws/src/SmithyRsSource/codegen-server-test/model/simple.smithy [29, 9]`.

Comment on lines 220 to 223
We will continue to deserialize the different parts of the HTTP message into
the regular Rust standard library types. However, just before the
deserialization function returns, we will convert the type into the wrapper
tuple struct that we will eventually be handed over to the operation handler.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow the motivation for doing the deserialization in two steps rather than one? It can easily lead to excessive allocations/heap pressure if the conversions don't take care to re-use previous allocations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we do it in one step? Ultimately the wrapper tuple struct holds e.g. a String, so don't we first have to deserialize the bytes slice into a String?

Comment on lines 297 to 299
We will enforce the length constraint by calling `len()` on Rust's `Vec`
(`list` and `set` shapes), `HashMap` (`map` shapes), `String` (`string`
shapes), and our [`aws_smithy_types::Blob`] (`bytes` shapes).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we specifically couldn't use len for String?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😅

Unresolved questions
--------------------

1. Should we code-generate unsigned integer types (`u16`, `u32`, `u64`) when
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea, but it does mean that if the constraints change, the representation of the type also changes, and the server code may need to be adjusted accordingly to work with the new types. If we're okay with a constraint change leading to required changes to user (server) code, then 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. I'm of the opinion that if you add / remove a constraint, you're backwards-incompatibly affecting your service's behavior, so a backwards-incompatible change in server user code seems appropriate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a mostly okay line of thinking but it does imply that a model is written by a service team for a service they own, and in practice that is not the only way models are written. A common pattern has been that one team writes a common service interface that is then vended to many unrelated teams, who write service implementations that conform to this specification. For these scenarios, model updates resulting in code generation changes are much more similar to what you would model as client code generation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it in the best interest of the teams that implement the service to be alerted if the model to which they must conform is going to change backwards-incompatibly? Breaking their build is the best kind of notification :)

Although I'll concede that if they have their integer types explicitly typed in many signatures that they eventually use to construct the constrained tuple struct, a change from e.g. i32 to u32 might incur unnecessary churn.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think breaking the build is fine if a constraint has been tightened, but if it's been loosened, that's really unfair. In my opinion, the types in the target language should be derived solely from the smithy types, and they shouldn't be impacted by the constraint traits outside of @required/@default.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to regard those "custom Rust features" as Smithy traits that live outside of the official Smithy implementation?

For example one could decide to create the following trait

@trait(selector: "integer")
structure rustInteger {
      @required
      int: String
}

@rustInteger("u8")
Integer MyInteger

and them modify smithy-rs to support that rustInteger trait?

That would allow a team using Rust to apply custom traits to a model shared with other team via the external trait application.

Copy link
Contributor Author

@david-perez david-perez Feb 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oddg Certainly, you can do all sorts of custom traits in Smithy. I think I like more the idea of having an @unsigned trait for this.


One thing to note is that if we were to generate unsigned types we would be expanding the range of accepted values by Smithy. For example, an integer shape in Smithy is a 32-bit integer with range from -2^31 to (2^31)-1. That's exactly the range of i32 in Rust. If we tack on @unsigned or @range(min: 0) to the shape, it's unclear that it's too helpful to generate an u32, because the upper bound of that type is (2^32)-1. The wrapper tuple MyInteger(u32) struct would only have to check the upper bound.

I'm now inclined to drop this feature and have range on integers not affect whether we generate signed or unsigned integer types.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you wanted to, you could impl an .unsigned() method when the model allowed to enable converting from i32 to u32 fearlessly

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth mentioning that the clients should also support this if the server does, or there will be issues when a server responds with something outside of the signed int range.

than 0.
2. In request deserialization, should we fail with the first violation and
immediately render a response, or attempt to parse the entire request and
provide a complete and structured report?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend having a customizeable max-limit on the number of validation errors that can be encountered before an early return, with the option to set it to None or something for no limit.

2. In request deserialization, should we fail with the first violation and
immediately render a response, or attempt to parse the entire request and
provide a complete and structured report?
3. Should we provide a mechanism for the service implementer to construct a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the answer to this is definitely yes. Even just for testing, having the ability to create invalid values is invaluable, as long as it's behind a controlled interface. I agree though that we probably don't want to encourage its use for transient values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What (testing) scenarios do you think would make this feature valuable?

struct is in a transient invalid state. However:
- (2) is arguably a modelling mistake and a separate struct to represent
the transient state would be a better approach,
- the user can always use `unsafe` Rust to bypass the validation; and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How?

Copy link
Contributor Author

@david-perez david-perez Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrapper types that we're generating are not particularly complex so the memory layout could be predictable, so I was thinking that a sufficiently motivated user that wanted to bypass validation could use std::mem::transmute.


[`NonZeroU64`]: https://doc.rust-lang.org/std/num/struct.NonZeroU64.html

Alternative design
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the primary design, though with the addendum that I think we should provide conversions back to the primitive type. Something like fn into_unconstrained.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, need to add this to the doc. I was thinking of into_inner but into_unconstrained is more explicit.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also prefer the primary design as it fosters correctness.

Comment on lines 169 to 170
certain protocols, for example. The format of the error messages will _not_ be
configurable by service implementers and will be

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preceding frameworks have allowed validation to be disabled or customized - from message all the way up to a completely different error. If a service implementer wanted to take control of the display of validation failures without opting out of the Rust SSDK validation framework entirely, how would they do so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A completely different error might be very challenging, since the error struct provided / configured by the user would have to compile against everything else. A plugin system of some sorts would have to be built.

As for customizing the format of error messages, that's something we could add support for. I'd imagine the user would have to provide custom templates in the configuration of our plugin in smithy-build.json (?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the sort of thing that is much easier to do if you don't bake validation into the type system - in TS, handlers can be configured to accept a callback that is passed an array of ValidationFailure objects, and the user can turn those into an error of their choice that is then rendered by the framework. They still get the benefit of validation without giving up the presentation of modeled exceptions coming from their service.

Which reminds me of another point - we have largely done away with synthetic exceptions in Smithy. Validation is no difference - are you requiring that a service's operations be associated with a particular error structure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in TS, handlers can be configured to accept a callback that is passed an array of ValidationFailure objects, and the user can turn those into an error of their choice that is then rendered by the framework.

Ah, I hadn't thought about that idea; I had imagined they'd want the errors to be configured ahead-of-time, not at runtime. That's neat. I don't see why we would not be able to build it into the Rust framework. We could have users return structs (or functions that return structs) that implement std::error::Error (or some trait of our own) for each of the ValidationFailures in that callback, and we would use those representations when rendering the response.

Which reminds me of another point - we have largely done away with synthetic exceptions in Smithy.

What are synthetic exceptions?

are you requiring that a service's operations be associated with a particular error structure?

If the operation in the Smithy model is fallible (has an errors field), we require that the operation handlers return a Result<T, E>, where the error has to be the enum that we generate holding all the variants of their Smithy errors field.

Copy link

@adamthom-amzn adamthom-amzn Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthetic exceptions are an idea from a legacy framework - exceptions that every service throws that don't have to be declared in the model. In that framework, ValidationException is synthetic, and can always be thrown.

In SSDKs, we'd like to restrict that idea to only things that are unavoidably thrown, either by virtue of "this is how programming works" (SerializationException, InternalFailure) or the underlying protocol (incorrect values for Accept, etc).

In TS, you have two options:

  • If every operation in the service has smithy.framework#ValidationException attached, and no customization is done in smithy-build.json, then the default validation behavior is engaged and the handlers cannot customize error rendering.
  • If smithy-build.json has disableDefaultValidation set to true, code generation adds a required parameter to handlers for a callback that can turn ValidationFailure[] into an error (or undefined, if it wants to suppress the error)

If you don't attach smithy.framework#ValidationException to every operation in the service and you don't disable default validation in smithy-build, then code generation fails.

All this to say that you have to guarantee your validation failures result in modeled exceptions being rendered, and not unmodeled 400s.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for having the option to provide the function doing ValidationFailure[] -> CustomErrorAlwaysMappedTo400

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have to guarantee your validation failures result in modeled exceptions being rendered, and not unmodeled 400s.

I agree. Otherwise the server implementation breaks the Smithy contract. (I am actually curious about the current behavior if the input does not parse at all.)

As you mentioned, one approach to let the user choose which error the operation will return is to generate for each operation input a type for validation error and ask the user to provide a function mapping this type to the operation error type.

Taking the weather forecast example it would look like.

    enum GetCityInputError {
        CityId(CityIdValidationError)
    }

    enum CityIdValidationError {
        Pattern(String)
    }

A user would have to implement a mapping fn(GetCityInputError) -> GetCityError.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamthom-amzn I didn't know about ValidationException, I don't see it documented in the docsite. Is the intention for this shape to be special, to be the default way for service frameworks to report validation errors?

adds a required parameter to handlers for a callback that can turn ValidationFailure[] into an error

I'm assuming this error is modeled and associated to the operation. If the operation in the model does not define at least one error, code generation fails, right?


@oddg Would that mapping be static per operation (i.e. two identical invalid requests resulting in the same GetCityError)? Would it be a simple mapping (i.e. no access to state)?

If I were to implement this, I'm thinking that if default validation is disabled (e.g. in smithy-build.json) we could generate the operation registry to take in both the operation handler and the error mapping function per operation registration. Any other ideas as to where the user could provide the error mapping function?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention for this shape to be special, to be the default way for service frameworks to report validation errors?

Special? No, we're trying not to have anything be special. Think of it as a built-in convenience that service frameworks are free to use as a default :)

I'm assuming this error is modeled and associated to the operation. If the operation in the model does not define at least one error, code generation fails, right?

This would be a fine behavior to have. Another is to just use your normal error serialization logic, which should turn any exception that is not associated with the operation into an InternalFailure.

I am actually curious about the current behavior if the input does not parse at all.

@oddg by spec, here meaning protocol tests, unparseable input should result in a 400 with the error code of SerializationException. It's a synthetic, which as I outlined above, means non-modeled (and does not need to have a corresponding specialized type generated client-side).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that mapping be static per operation (i.e. two identical invalid requests resulting in the same GetCityError)? Would it be a simple mapping (i.e. no access to state)?

Yes. In my opinion, it makes sense that two identical invalid requests result in the same errors. I agree that it should not depend on any state. (Any error that is is state dependent should be generated in the core implementation of the handler for that operation.)

We could generate the operation registry to take in both the operation handler and the error mapping function per operation registration.

That makes a lot of sense to me.

by spec, here meaning protocol tests, unparseable input should result in a 400 with the error code of SerializationException. It's a synthetic, which as I outlined above, means non-modeled (and does not need to have a corresponding specialized type generated client-side).

Thanks.

Comment on lines 63 to 64
The only constraint trait enforcement that is generated by smithy-rs clients
should be and is the `enum` trait, which renders Rust `enum`s.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth mentioning that the enum trait is also enforced by smithy-rs server?

Comment on lines 169 to 170
certain protocols, for example. The format of the error messages will _not_ be
configurable by service implementers and will be
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have to guarantee your validation failures result in modeled exceptions being rendered, and not unmodeled 400s.

I agree. Otherwise the server implementation breaks the Smithy contract. (I am actually curious about the current behavior if the input does not parse at all.)

As you mentioned, one approach to let the user choose which error the operation will return is to generate for each operation input a type for validation error and ask the user to provide a function mapping this type to the operation error type.

Taking the weather forecast example it would look like.

    enum GetCityInputError {
        CityId(CityIdValidationError)
    }

    enum CityIdValidationError {
        Pattern(String)
    }

A user would have to implement a mapping fn(GetCityInputError) -> GetCityError.


[`NonZeroU64`]: https://doc.rust-lang.org/std/num/struct.NonZeroU64.html

Alternative design
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also prefer the primary design as it fosters correctness.

Unresolved questions
--------------------

1. Should we code-generate unsigned integer types (`u16`, `u32`, `u64`) when
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to regard those "custom Rust features" as Smithy traits that live outside of the official Smithy implementation?

For example one could decide to create the following trait

@trait(selector: "integer")
structure rustInteger {
      @required
      int: String
}

@rustInteger("u8")
Integer MyInteger

and them modify smithy-rs to support that rustInteger trait?

That would allow a team using Rust to apply custom traits to a model shared with other team via the external trait application.

2. Conversions into the tuple struct are fallible (`try_from()` instead of
`from()`). These errors will result in a `MyStructValidationError`, wrapped
in the runtime type error
`aws_smithy_http_server::rejection::ConstraintViolation` rejection. The
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be a way to hook these different failures for metrics so that service owners can discern between failure to parse and failure to validate in their metrics?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; the latter will be Deserialize rejections containing ConstraintViolation rejections, the former will contain other inner rejections, so it will be possible to distinguish.

set of values it can accept:

```rust
pub struct NiceString(String);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's worth calling out that adding a constraint trait is now a breaking change for servers since it adds this type wrapper. I haven't thought about the trade off deeply, but it seems worth mentioning / considering since it will make it a bit more of a hassle to add constraints retroactively

Unresolved questions
--------------------

1. Should we code-generate unsigned integer types (`u16`, `u32`, `u64`) when
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you wanted to, you could impl an .unsigned() method when the model allowed to enable converting from i32 to u32 fearlessly

@david-perez david-perez mentioned this pull request May 20, 2022
23 tasks
@jdisanti jdisanti added the rfc Marks a PR as an RFC label Nov 10, 2022
david-perez added a commit that referenced this pull request Nov 15, 2022
This patchset, affectionately called "Builders of builders", lays the
groundwork for fully implementing [Constraint traits] in the server SDK
generator. [The RFC] illustrates what the end goal looks like, and is
recommended prerrequisite reading to understanding this cover letter.

This commit makes the sever deserializers work with _unconstrained_ types
during request parsing, and only after the entire request is parsed are
constraints enforced. Values for a constrained shape are stored in the
correspondingly unconstrained shape, and right before the operation input is
built, the values are constrained via a `TryFrom<UnconstrainedShape> for
ConstrainedShape` implementation that all unconstrained types enjoy. The
service owner only interacts with constrained types, the unconstrained ones are
`pub(crate)` and for use by the framework only.

In the case of structure shapes, the corresponding unconstrained shape is their
builders. This is what gives this commit its title: during request
deserialization, arbitrarily nested structures are parsed into _builders that
hold builders_. Builders keep track of whether their members are constrained or
not by storing its members in a `MaybeConstrained`
[Cow](https://doc.rust-lang.org/std/borrow/enum.Cow.html)-like `enum` type:

```rust
pub(crate) trait Constrained {
    type Unconstrained;
}

#[derive(Debug, Clone)]
pub(crate) enum MaybeConstrained<T: Constrained> {
    Constrained(T),
    Unconstrained(T::Unconstrained),
}
```

Consult the documentation for the generator in `ServerBuilderGenerator.kt` for
more implementation details and for the differences with the builder types the
server has been using, generated by `BuilderGenerator.kt`, which after this
commit are exclusively used by clients.

Other shape types, when they are constrained, get generated with their
correspondingly unconstrained counterparts. Their Rust types are essentially
wrapper newtypes, and similarly enjoy `TryFrom` converters to constrain them.
See the documentation in `UnconstrainedShapeSymbolProvider.kt` for details and
an example.

When constraints are not met, the converters raise _constraint violations_.
These are currently `enum`s holding the _first_ encountered violation.

When a shape is _transitively but not directly_ constrained, newtype wrappers
are also generated to hold the nested constrained values. To illustrate their
need, consider for example a list of `@length` strings. Upon request parsing,
the server deserializers need a way to hold a vector of unconstrained regular
`String`s, and a vector of the constrained newtyped `LengthString`s. The former
requirement is already satisfied by the generated unconstrained types, but for
the latter we need to generate an intermediate constrained
`ListUnconstrained(Vec<LengthString>)` newtype that will eventually be
unwrapped into the `Vec<LengthString>` the user is handed. This is the purpose
of the `PubCrate*` generators: consult the documentation in
`PubCrateConstrainedShapeSymbolProvider.kt`,
`PubCrateConstrainedCollectionGenerator.kt`, and
`PubCrateConstrainedMapGenerator.kt` for more details. As their name implies,
all of these types are `pub(crate)`, and the user never interacts with them.

For users that would not like their application code to make use of constrained
newtypes for their modeled constrained shapes, a `codegenConfig` setting
`publicConstrainedTypes` has been added. They opt out of these by setting it to
`false`, and use the inner types directly: the framework will still enforce
constraints upon request deserialization, but once execution enters an
application handler, the user is on their own to honor (or not) the modeled
constraints. No user interest has been expressed for this feature, but I expect
we will see demand for it. Moreover, it's a good stepping stone for users that
want their services to honor constraints, but are not ready to migrate their
application code to constrained newtypes. As for how it's implemented, several
parts of the codebase inspect the setting and toggle or tweak generators based
on its value. Perhaps the only detail worth mentioning in this commit message
is that the structure shape builder types are generated by a much simpler and
entirely different generator, in
`ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt`. Note that this
builder _does not_ enforce constraints, except for `required` and `enum`, which
are always (and already) baked into the type system. When
`publicConstrainedTypes` is disabled, this is the builder that end users
interact with, while the one that enforces all constraints,
`ServerBuilderGenerator`, is now generated as `pub(crate)` and left for
exclusive use by the deserializers. See the relevant documentation for the
details and differences among the builder types.

As proof that these foundations are sound, this commit also implements the
`length` constraint trait on Smithy map and string shapes. Likewise, the
`required` and `enum` traits, which were already baked in the generated types
as non-`Option`al and `enum` Rust types, respectively, are now also treated
like the rest of constraint traits upon request deserialization. See the
documentation in `ConstrainedMapGenerator.kt` and
`ConstrainedStringGenerator.kt` for details.

The rest of the constraint traits and target shapes are left as an exercise to
the reader, but hopefully the reader has been convinced that all of them can be
enforced within this framework, paving the way for straightforward
implementations. The diff is already large as it is. Any reamining work is
being tracked in #1401; this and other issues are referenced in the code as
TODOs.

So as to not give users the impression that the server SDK plugin _fully_
honors constraints as per the Smithy specification, a validator in
`ValidateUnsupportedConstraintsAreNotUsed.kt` has been added. This traverses
the model and detects yet-unsupported parts of the spec, aborting code
generation and printing informative warnings referencing the relevant tracking
issues. This is a regression in that models that used constraint traits
previously built fine (even though the constraint traits were silently not
being honored), and now they will break. To unblock generation of these models,
this commit adds another `codegenConfig` setting,
`ignoreUnsupportedConstraints`, that users can opt into.

Closes #1714.

Testing
-------

Several Kotlin unit test classes exercising the finer details of the added
generators and symbol providers have been added. However, the best way to test
is to generate server SDKs from models making use of constraint traits. The
biggest assurances come from the newly added `constraints.smithy` model, an
"academic" service that _heavily_ exercises constraint traits. It's a
`restJson1` service that also tests binding of constrained shapes to different
parts of the HTTP message. Deeply nested hierarchies and recursive shapes are
also featured.

```sh
./gradlew -P modules='constraints' codegen-server-test:build
```

This model is _additionally_ generated in CI with the `publicConstrainedTypes`
setting disabled:

```sh
./gradlew -P modules='constraints_without_public_constrained_types' codegen-server-test:build
``````

Similarly, models using currently unsupported constraints are now being
generated with the `ignoreUnsupportedConstraints` setting enabled.

See `codegen-server-test/build.gradle.kts` for more details.

[Constraint traits]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html
[The RFC]: #1199
@david-perez
Copy link
Contributor Author

I've updated the RFC to reflect what we ended up implementing.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@david-perez david-perez enabled auto-merge (squash) December 1, 2022 15:37
@github-actions
Copy link

github-actions bot commented Dec 1, 2022

A new generated diff is ready to view.

A new doc preview is ready to view.

@david-perez david-perez merged commit 7c86ecf into main Dec 1, 2022
@david-perez david-perez deleted the davidpz-constraint-traits-rfc branch December 1, 2022 16:07
aws-sdk-rust-ci pushed a commit to awslabs/aws-sdk-rust that referenced this pull request Dec 14, 2022
This patchset, affectionately called "Builders of builders", lays the
groundwork for fully implementing [Constraint traits] in the server SDK
generator. [The RFC] illustrates what the end goal looks like, and is
recommended prerrequisite reading to understanding this cover letter.

This commit makes the sever deserializers work with _unconstrained_ types
during request parsing, and only after the entire request is parsed are
constraints enforced. Values for a constrained shape are stored in the
correspondingly unconstrained shape, and right before the operation input is
built, the values are constrained via a `TryFrom<UnconstrainedShape> for
ConstrainedShape` implementation that all unconstrained types enjoy. The
service owner only interacts with constrained types, the unconstrained ones are
`pub(crate)` and for use by the framework only.

In the case of structure shapes, the corresponding unconstrained shape is their
builders. This is what gives this commit its title: during request
deserialization, arbitrarily nested structures are parsed into _builders that
hold builders_. Builders keep track of whether their members are constrained or
not by storing its members in a `MaybeConstrained`
[Cow](https://doc.rust-lang.org/std/borrow/enum.Cow.html)-like `enum` type:

```rust
pub(crate) trait Constrained {
    type Unconstrained;
}

#[derive(Debug, Clone)]
pub(crate) enum MaybeConstrained<T: Constrained> {
    Constrained(T),
    Unconstrained(T::Unconstrained),
}
```

Consult the documentation for the generator in `ServerBuilderGenerator.kt` for
more implementation details and for the differences with the builder types the
server has been using, generated by `BuilderGenerator.kt`, which after this
commit are exclusively used by clients.

Other shape types, when they are constrained, get generated with their
correspondingly unconstrained counterparts. Their Rust types are essentially
wrapper newtypes, and similarly enjoy `TryFrom` converters to constrain them.
See the documentation in `UnconstrainedShapeSymbolProvider.kt` for details and
an example.

When constraints are not met, the converters raise _constraint violations_.
These are currently `enum`s holding the _first_ encountered violation.

When a shape is _transitively but not directly_ constrained, newtype wrappers
are also generated to hold the nested constrained values. To illustrate their
need, consider for example a list of `@length` strings. Upon request parsing,
the server deserializers need a way to hold a vector of unconstrained regular
`String`s, and a vector of the constrained newtyped `LengthString`s. The former
requirement is already satisfied by the generated unconstrained types, but for
the latter we need to generate an intermediate constrained
`ListUnconstrained(Vec<LengthString>)` newtype that will eventually be
unwrapped into the `Vec<LengthString>` the user is handed. This is the purpose
of the `PubCrate*` generators: consult the documentation in
`PubCrateConstrainedShapeSymbolProvider.kt`,
`PubCrateConstrainedCollectionGenerator.kt`, and
`PubCrateConstrainedMapGenerator.kt` for more details. As their name implies,
all of these types are `pub(crate)`, and the user never interacts with them.

For users that would not like their application code to make use of constrained
newtypes for their modeled constrained shapes, a `codegenConfig` setting
`publicConstrainedTypes` has been added. They opt out of these by setting it to
`false`, and use the inner types directly: the framework will still enforce
constraints upon request deserialization, but once execution enters an
application handler, the user is on their own to honor (or not) the modeled
constraints. No user interest has been expressed for this feature, but I expect
we will see demand for it. Moreover, it's a good stepping stone for users that
want their services to honor constraints, but are not ready to migrate their
application code to constrained newtypes. As for how it's implemented, several
parts of the codebase inspect the setting and toggle or tweak generators based
on its value. Perhaps the only detail worth mentioning in this commit message
is that the structure shape builder types are generated by a much simpler and
entirely different generator, in
`ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt`. Note that this
builder _does not_ enforce constraints, except for `required` and `enum`, which
are always (and already) baked into the type system. When
`publicConstrainedTypes` is disabled, this is the builder that end users
interact with, while the one that enforces all constraints,
`ServerBuilderGenerator`, is now generated as `pub(crate)` and left for
exclusive use by the deserializers. See the relevant documentation for the
details and differences among the builder types.

As proof that these foundations are sound, this commit also implements the
`length` constraint trait on Smithy map and string shapes. Likewise, the
`required` and `enum` traits, which were already baked in the generated types
as non-`Option`al and `enum` Rust types, respectively, are now also treated
like the rest of constraint traits upon request deserialization. See the
documentation in `ConstrainedMapGenerator.kt` and
`ConstrainedStringGenerator.kt` for details.

The rest of the constraint traits and target shapes are left as an exercise to
the reader, but hopefully the reader has been convinced that all of them can be
enforced within this framework, paving the way for straightforward
implementations. The diff is already large as it is. Any reamining work is
being tracked in #1401; this and other issues are referenced in the code as
TODOs.

So as to not give users the impression that the server SDK plugin _fully_
honors constraints as per the Smithy specification, a validator in
`ValidateUnsupportedConstraintsAreNotUsed.kt` has been added. This traverses
the model and detects yet-unsupported parts of the spec, aborting code
generation and printing informative warnings referencing the relevant tracking
issues. This is a regression in that models that used constraint traits
previously built fine (even though the constraint traits were silently not
being honored), and now they will break. To unblock generation of these models,
this commit adds another `codegenConfig` setting,
`ignoreUnsupportedConstraints`, that users can opt into.

Closes #1714.

Testing
-------

Several Kotlin unit test classes exercising the finer details of the added
generators and symbol providers have been added. However, the best way to test
is to generate server SDKs from models making use of constraint traits. The
biggest assurances come from the newly added `constraints.smithy` model, an
"academic" service that _heavily_ exercises constraint traits. It's a
`restJson1` service that also tests binding of constrained shapes to different
parts of the HTTP message. Deeply nested hierarchies and recursive shapes are
also featured.

```sh
./gradlew -P modules='constraints' codegen-server-test:build
```

This model is _additionally_ generated in CI with the `publicConstrainedTypes`
setting disabled:

```sh
./gradlew -P modules='constraints_without_public_constrained_types' codegen-server-test:build
``````

Similarly, models using currently unsupported constraints are now being
generated with the `ignoreUnsupportedConstraints` setting enabled.

See `codegen-server-test/build.gradle.kts` for more details.

[Constraint traits]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html
[The RFC]: smithy-lang/smithy-rs#1199
thomas-k-cameron added a commit to thomas-k-cameron/smithy-rs that referenced this pull request Apr 9, 2023
* Add error refactor changelog entries and re-export `DisplayErrorContext` (#1951)

* Re-export `DisplayErrorContext`
* Update changelog

* Do not use deprecated from_timestamp from chrono (#1980)

* Do not use deprecated from_timestamp from chrono

CI is failing because
[from_timestamp](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html#method.from_timestamp)
is now deprecated.

Signed-off-by: Daniele Ahmed <[email protected]>

* Update changelog

Signed-off-by: Daniele Ahmed <[email protected]>

* Fix error

Signed-off-by: Daniele Ahmed <[email protected]>

* Use with_ymd_and_hms

Signed-off-by: Daniele Ahmed <[email protected]>

Signed-off-by: Daniele Ahmed <[email protected]>

* Remove left behind trailing whitespace from instrumentation closure (#1975)

* Fix `update-sdk-next` GitHub Actions workflow (#1979)

* Feature: configurable connectors for AWS clients (#1918)

* feature: make HTTP connectors configurable

* add: test for HTTP connector configuration customization
add: impl<B> From<TestConnection<B>> for HttpConnector
add: impl From<CaptureRequestHandler> for HttpConnector
add: impl From<NeverConnector> for HttpConnector
add: impl From<ReplayingConnection> for HttpConnector

* add: to_vec method to AggregatedBytes
update: method param names of FluentClientGenerics.sendBounds to be more explicit
update: restructure s3/s3control tests to be uniform in structure

* update: CHANGELOG.next.toml
update: codegen `impl From<&SdkConfig> for Builder` to support HTTP connectors

* update: CHANGELOG entry references

* add: missing copyright header

* fix: clippy lint

* format: run cargo fmt

* format: run cargo fmt on aws_smithy_client::dvr modules

* format: run ktlintFormat

* refactor: use from_conf instead of from_conf_conn
remove: from_conf_conn

* update: impl From<SmithyConnector> for HttpConnector
remove: other From<T> for HttpConnector impls
update: HttpConnector config setter examples

* update: CHANGELOG.next.toml

* Update CHANGELOG.next.toml

Co-authored-by: John DiSanti <[email protected]>

* update: CHANGELOG.next.toml
remove: obsolete test
update: `ConfigLoader::http_connector` setter method

* Update CHANGELOG.next.toml

Co-authored-by: John DiSanti <[email protected]>

* Update CHANGELOG.next.toml

Co-authored-by: John DiSanti <[email protected]>

* Update CHANGELOG.next.toml

Co-authored-by: John DiSanti <[email protected]>

* Apply suggestions from code review

Co-authored-by: John DiSanti <[email protected]>

* update: aws_config::loader::ConfigLoader doc comments
update: CHANGELOG.next.toml examples

* fix: doc issues
add: reëxport aws_smithy_types::endpoint module to aws-config

* format: run rustfmt in the weird CI way to get it to actually format.

* fix: incorrect reëxport

* add: "aws_smithy_http::endpoint" to allowed external types for aws-config

* update: move `hyper-rustls` to deps so that it doesn't break exotic arch CI check

* remove: `hyper-rustls` dep because it's not actually needed

* fix: aws-types dep issue blocking exotic arch CI check

* fix: broken doc comment

Co-authored-by: John DiSanti <[email protected]>

* Service with ConnectInfo (#1955)

* Service with ConnectInfo

Signed-off-by: Daniele Ahmed <[email protected]>

* Use `escape` on the body in `@httpRequestTest` protocol test generation (#1949)

* Builders of builders (#1342)

This patchset, affectionately called "Builders of builders", lays the
groundwork for fully implementing [Constraint traits] in the server SDK
generator. [The RFC] illustrates what the end goal looks like, and is
recommended prerrequisite reading to understanding this cover letter.

This commit makes the sever deserializers work with _unconstrained_ types
during request parsing, and only after the entire request is parsed are
constraints enforced. Values for a constrained shape are stored in the
correspondingly unconstrained shape, and right before the operation input is
built, the values are constrained via a `TryFrom<UnconstrainedShape> for
ConstrainedShape` implementation that all unconstrained types enjoy. The
service owner only interacts with constrained types, the unconstrained ones are
`pub(crate)` and for use by the framework only.

In the case of structure shapes, the corresponding unconstrained shape is their
builders. This is what gives this commit its title: during request
deserialization, arbitrarily nested structures are parsed into _builders that
hold builders_. Builders keep track of whether their members are constrained or
not by storing its members in a `MaybeConstrained`
[Cow](https://doc.rust-lang.org/std/borrow/enum.Cow.html)-like `enum` type:

```rust
pub(crate) trait Constrained {
    type Unconstrained;
}

#[derive(Debug, Clone)]
pub(crate) enum MaybeConstrained<T: Constrained> {
    Constrained(T),
    Unconstrained(T::Unconstrained),
}
```

Consult the documentation for the generator in `ServerBuilderGenerator.kt` for
more implementation details and for the differences with the builder types the
server has been using, generated by `BuilderGenerator.kt`, which after this
commit are exclusively used by clients.

Other shape types, when they are constrained, get generated with their
correspondingly unconstrained counterparts. Their Rust types are essentially
wrapper newtypes, and similarly enjoy `TryFrom` converters to constrain them.
See the documentation in `UnconstrainedShapeSymbolProvider.kt` for details and
an example.

When constraints are not met, the converters raise _constraint violations_.
These are currently `enum`s holding the _first_ encountered violation.

When a shape is _transitively but not directly_ constrained, newtype wrappers
are also generated to hold the nested constrained values. To illustrate their
need, consider for example a list of `@length` strings. Upon request parsing,
the server deserializers need a way to hold a vector of unconstrained regular
`String`s, and a vector of the constrained newtyped `LengthString`s. The former
requirement is already satisfied by the generated unconstrained types, but for
the latter we need to generate an intermediate constrained
`ListUnconstrained(Vec<LengthString>)` newtype that will eventually be
unwrapped into the `Vec<LengthString>` the user is handed. This is the purpose
of the `PubCrate*` generators: consult the documentation in
`PubCrateConstrainedShapeSymbolProvider.kt`,
`PubCrateConstrainedCollectionGenerator.kt`, and
`PubCrateConstrainedMapGenerator.kt` for more details. As their name implies,
all of these types are `pub(crate)`, and the user never interacts with them.

For users that would not like their application code to make use of constrained
newtypes for their modeled constrained shapes, a `codegenConfig` setting
`publicConstrainedTypes` has been added. They opt out of these by setting it to
`false`, and use the inner types directly: the framework will still enforce
constraints upon request deserialization, but once execution enters an
application handler, the user is on their own to honor (or not) the modeled
constraints. No user interest has been expressed for this feature, but I expect
we will see demand for it. Moreover, it's a good stepping stone for users that
want their services to honor constraints, but are not ready to migrate their
application code to constrained newtypes. As for how it's implemented, several
parts of the codebase inspect the setting and toggle or tweak generators based
on its value. Perhaps the only detail worth mentioning in this commit message
is that the structure shape builder types are generated by a much simpler and
entirely different generator, in
`ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt`. Note that this
builder _does not_ enforce constraints, except for `required` and `enum`, which
are always (and already) baked into the type system. When
`publicConstrainedTypes` is disabled, this is the builder that end users
interact with, while the one that enforces all constraints,
`ServerBuilderGenerator`, is now generated as `pub(crate)` and left for
exclusive use by the deserializers. See the relevant documentation for the
details and differences among the builder types.

As proof that these foundations are sound, this commit also implements the
`length` constraint trait on Smithy map and string shapes. Likewise, the
`required` and `enum` traits, which were already baked in the generated types
as non-`Option`al and `enum` Rust types, respectively, are now also treated
like the rest of constraint traits upon request deserialization. See the
documentation in `ConstrainedMapGenerator.kt` and
`ConstrainedStringGenerator.kt` for details.

The rest of the constraint traits and target shapes are left as an exercise to
the reader, but hopefully the reader has been convinced that all of them can be
enforced within this framework, paving the way for straightforward
implementations. The diff is already large as it is. Any reamining work is
being tracked in #1401; this and other issues are referenced in the code as
TODOs.

So as to not give users the impression that the server SDK plugin _fully_
honors constraints as per the Smithy specification, a validator in
`ValidateUnsupportedConstraintsAreNotUsed.kt` has been added. This traverses
the model and detects yet-unsupported parts of the spec, aborting code
generation and printing informative warnings referencing the relevant tracking
issues. This is a regression in that models that used constraint traits
previously built fine (even though the constraint traits were silently not
being honored), and now they will break. To unblock generation of these models,
this commit adds another `codegenConfig` setting,
`ignoreUnsupportedConstraints`, that users can opt into.

Closes #1714.

Testing
-------

Several Kotlin unit test classes exercising the finer details of the added
generators and symbol providers have been added. However, the best way to test
is to generate server SDKs from models making use of constraint traits. The
biggest assurances come from the newly added `constraints.smithy` model, an
"academic" service that _heavily_ exercises constraint traits. It's a
`restJson1` service that also tests binding of constrained shapes to different
parts of the HTTP message. Deeply nested hierarchies and recursive shapes are
also featured.

```sh
./gradlew -P modules='constraints' codegen-server-test:build
```

This model is _additionally_ generated in CI with the `publicConstrainedTypes`
setting disabled:

```sh
./gradlew -P modules='constraints_without_public_constrained_types' codegen-server-test:build
``````

Similarly, models using currently unsupported constraints are now being
generated with the `ignoreUnsupportedConstraints` setting enabled.

See `codegen-server-test/build.gradle.kts` for more details.

[Constraint traits]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html
[The RFC]: https://github.com/awslabs/smithy-rs/pull/1199

* Revamp errors in `aws-smithy-http` (#1884)

* Revamp errors in `aws-smithy-json` (#1888)

* Revamp errors in `aws-smithy-types` (#1893)

* Revamp errors in `aws-smithy-types-convert` and `aws-smithy-xml` (#1917)

* Revamp errors in AWS runtime crates (#1922)

Revamp errors in:
  * `aws-types`
  * `aws-endpoint`
  * `aws-http`
  * `aws-sig-auth`
  * `aws-inlineable`

* Revamp errors in `aws-config` (#1934)

* Make `SdkError::into_service_error` infallible (#1974)

* Implement RFC23 - Evolve the new service builder API (#1954)

* Implement RFC 23, with the exception of PluginBuilder

* Update documentation.

* Elide implementation details in `Upgradable`.

* Update wording in docs to avoid personal pronouns.

* Update `Upgradable`'s documentation.

* Template the service builder name.

* Template MissingOperationsError directly.

* Code-generate an array directly.

* Update design/src/server/anatomy.md

Co-authored-by: david-perez <[email protected]>

* Use `rust` where we do not need templating.

* Remove unused variable.

* Add `expect` message to point users at our issue board in case a panic slips through.

* Typo.

* Update `expect` error message.

* Refactor the `for` loop in ``requestSpecMap` into an `associateWith` call.

* Fix new pokemon bin example.

* Fix new pokemon bin example.

* Fix unresolved symbolProvider.

* Assign the `expect` error message to a variable.

* Omit additional generic parameters in Upgradable when it's first introduced.

Co-authored-by: david-perez <[email protected]>

* Fix simple.smithy (#1991)

Signed-off-by: Daniele Ahmed <[email protected]>

* Add support for Endpoints 2.0 Parameters (#1953)

* Add support for Endpoints 2.0 Parameters

This commit adds `EndpointDecorator` which injects Endpoint
parameters in to the operation property bag. These can come from
an ordered list of sources—this wires them all up. To facilitate
testing, this diff also writes the parameters into the property
bag during operation generation.

* remove println

* CR Feedback

* Implement RFC 23, Part 2 - Plugin pipeline (#1971)

* Implement RFC 23, with the exception of PluginBuilder

* Update documentation.

* Avoid star re-exports.

* Elide implementation details in `Upgradable`.

* Update wording in docs to avoid personal pronouns.

* Update `Upgradable`'s documentation.

* Template the service builder name.

* Template MissingOperationsError directly.

* Code-generate an array directly.

* Sketch out the implementation of `PluginPipeline`.
Remove `PluginExt`.
Add a public constructor for `FilterByOperationName`.

* Ask for a `PluginPipeline` as input for the generated builder.

* Rename `add` to `push`.

* Remove Pluggable.
Rename `composer` module to `pipeline`.

* Remove all mentions of `Pluggable` from docs and examples.

* Fix punctuation.

* Rename variable from `composer` to `pipeline` in doc examples.

* Add a free-standing function for filtering.

* Rename.

* Rename.

* Update design/src/server/anatomy.md

Co-authored-by: david-perez <[email protected]>

* Use `rust` where we do not need templating.

* Remove unused variable.

* Add `expect` message to point users at our issue board in case a panic slips through.

* Typo.

* Update `expect` error message.

* Refactor the `for` loop in ``requestSpecMap` into an `associateWith` call.

* Remove unnecessary bound - since `new` is private, the condition is already enforced via `filter_by_operation_name`.

* Use `Self` in `new`.

* Rename `inner` to `into_inner`

* Rename `concat` to `append` to correctly mirror Vec's API terminology.

* Fix codegen to use renamed method.

* Cut down the public API surface to key methods for a sequence-like interface.

* Add a note about ordering.

* Add method docs.

* Add Default implementation.

* Fix new pokemon bin example.

* Fix new pokemon bin example.

* Fix code-generated builder.

* Fix unresolved symbolProvider.

* Assign the `expect` error message to a variable.

* Do not require a PluginPipeline as input to `builder_with_plugins`.

* Reverse plugin application order.

* Upgrade documentation.

* Add a test to verify that plugin layers are executed in registration order.

* Add license header.

* Update middleware.md

* Typo.

* Fix more builder() calls.

Co-authored-by: david-perez <[email protected]>

* Improve error doc comments around logging errors (#1990)

* Rework enum for forward-compatibility (#1945)

* Make enum forward-compatible

This commit implements the suggested approach described in
https://github.com/awslabs/smithy-rs/issues/627#issuecomment-995923092

The idea is that once the user writes a match expression against an enum
and assumes that an execution path comes to a particular match arm, we
should guarantee that when the user upgrades a version of SDK, the
execution path should come to the same match arm as before.

* Add unit test to ensure enums are forward-compatible

The test first mimics the user's interaction with the enum generated from
a model where the user writes a match expression on the enum, wishing to
hit the match arm corresponding to Variant3, which is not yet supported
by the model. The test then simulates a scenario where the user now has
access to the updated enum generated from the next version of the model
that does support Variant3.
The user's code should compile in both of the use cases.

* Generate rustdoc for enum's forward-compatibility

This commits generates rustdoc explaining to the users how they might
write a match expression against a generated enum so their code is
forward-compatible. While it is explained in
https://github.com/awslabs/smithy-rs/issues/627#issuecomment-995923092,
it can be helpful if the users can read it in rustdoc right below where
the enum's definition is shown.

* Make snippet in rustdoc text

This commit updates a rustdoc code snippet generated for an enum from
`rust,no_run` to `text`. We observed that the snippet fails to compile
in CI because the name of the enum was not fully qualified; code in
rustdoc is treated in a similar way that code in integration tests is
treated as being outside of a crate, but not part of the crate, which
means the name of the crate needs to be spelled out for a `use` statement
if we want to import the enum to the code in rustdoc. However, the name
of the crate is usually one-off, generated on the fly for testing, making
it not obvious to hard-code it or obtain it programmatically from within
Kotlin code.

* Suppress missing doc lint for UnknownVariantValue

This commit marks UnknownVariantValue as `#[allow(missing_docs)]` because
failing to do so will cause tests in CI to fail.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: Russell Cohen <[email protected]>

* Generate UnknownVariantValue via forInlineFun

This commit attempts to create UnknownVariantValue using
RuntimeType.forInlineFun. Prior to this commit, it was generated per enum
but that caused multiple definitions of UnknownVariantValue in a single
file as there can be multiple enums in the file.
By using RuntimeType.forInlineFun, we can generate a single UnknownVariantValue
per module and the enums within the module can refer to the same instance.

This commit, however, fails to pass the unit tests in EnumGeneratorTest.
The issue seems to be that a RustWriter used in the tests does not end up
calling the finalize method, failing to render inline functions.

* Replace "target == CodegenTarget.CLIENT" with a helper

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1014390811
but uses an existing helper CodegenTarget.renderUnknownVariant.

* Update EnumGeneratorTests to use TestWorkspace

This commit addresses failures for unit tests in EnumGeneratorTests.
Now that we use RuntimeType.forInlineFcn in EnumGenerator, we need
to ensure that the inline functions should be rendered to a Rust
source file during testing. RustWriter, used in EnumGeneratorTests
prior to this commit, was not capable of doing so. We thus update
EnumGeneratorTests to use TestWorkspace by which we ensure that the
inline functions are rendered during testing.

* Make sure to use the passed-in variable for shapeId

This commit fixes a copy-and-paste error introduced in 712c983. The
function should use the passed-in variable rather than the hard-coded
literal "test#SomeEnum".

* Address https://github.com/awslabs/smithy-rs/pull/1945\#discussion_r1014389852

* Update CHANGELOG.next.toml

* Update CHANGELOG.next.toml

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1017235315

* Avoid potential name collisions by UnknownVariantValue

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1017237745.
It changes the module in which the `UnknownVariantValue` is rendered.
Before the commit, it was emitted to the `model` module but that might cause
name collisions in the future when a Smithy model has a shape named
`UnknownVariantValue`. After this commit, we render it to the `types` module.

* Move re-exports from lib.rs to types.rs

This commit moves re-export statements in client crates from `lib.rs` to
`types.rs`. The motivation is that now that we render the struct
`UnknownVariantValue` in a separate file for the `types` module, we can
no longer have a `pub mod types {...}` block in `lib.rs` as it cannot
coexist with `pub mod types;`.

* Add docs on UnknownVariantValue

This commit adds public docs on `UnknownVariantValue`. Since it is
rendered in `types.rs`, the users may not find the `Unknown` variant
of an enum in the same file and may wonder what this struct is for when
seeing it in isolation.

* Update CHANGELOG.next.toml

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1020414459

* Update the module documentation for types

This commit updates rustdoc for the types module to reflect that fact
that the module now contains not only re-exports but also an auxiliary
struct `UnknownVariantValue`.

* Add extensions to run code block depending on the target

This commit introduces extensions on CodegenTarget that execute the block
of code depending on the target is for client or for server. These
extensions are intended to replace if-else expressions throughout the
codebase explicitly checking whether the target is for client or for server.
We do not update such if-else expressions all at once in this commit.
Rather, we are planning to make incremental changes to update them
opportunistically.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: John DiSanti <[email protected]>

* Move extensions into CodegenTarget as methods

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1021952411.
By moving extensions into the CodegenTarget enum, no separate import is
required to use them.

Co-authored-by: Saito <[email protected]>
Co-authored-by: Russell Cohen <[email protected]>
Co-authored-by: Yuki Saito <[email protected]>
Co-authored-by: John DiSanti <[email protected]>

* fix: use idiomatic method of setting smithy gradle plugin version (#1995)

* formatting: run code cleanup on entire project (#1993)

* formatting: run code cleanup on entire project

* revert: deletion of SdkSettings.defaultsConfigPath

* Improve `Endpoint` panic-safety and ergonomics (#1984)

- `Endpoint::set_endpoint` no longer panics when called on an endpoint
  without a scheme
- `Endpoint::mutable` and `Endpoint::immutable` now both return a result
  so that constructing an endpoint without a scheme is an error
- `Endpoint::mutable` and `Endpoint::immutable` both now take a string
  instead of a `Uri` as a convenience
- `Endpoint::mutable_uri` and `Endpoint::immutable_uri` were added
  to construct an endpoint directly from a `Uri`

* Remove `fromCoreProtocol` in favour of `as` downcasting (#2000)

* Render full shape IDs in server error responses (#1982)

RestJson1 implementations can denote errors in responses in several
ways. New server-side protocol implementations MUST use a header field
named `X-Amzn-Errortype`.

Note that the spec says that implementations SHOULD strip the error
shape ID's namespace. However, our server implementation renders the
full shape ID (including namespace), since some existing clients rely on
it to deserialize the error shape and fail if only the shape name is
present. This is compliant with the spec, see
https://github.com/awslabs/smithy/pull/1493.

See https://github.com/awslabs/smithy/issues/1494 too.

* fix: use new version of deprecated fn (#1994)

update: crate-hasher sha256 dep to 1.1

* update: CargoDependency companion fn names for smithy runtime crates (#1996)

* update: CargoDependency companion fn names for smithy runtime crates
rename: CargoDependency.asType to CargoDependency.toType
fix: errors in InlineDependency doc comment
formatting: run import optimizer for all kotlin files
fix: gradle issue with incorrectly named tasks

* fix: server test broken by removal of rustName

* Improve `Plugin` toolkit (#2003)

Co-authored-by: Julian Antonielli <[email protected]>
Co-authored-by: Luca Palmieri <[email protected]>

* Multiple FromParts in handlers (#1999)

* Multiple FromParts in handlers

Handlers can have up to 8 extensions:
```
pub async fn handler(
    input: input::Input,
    ext0: Extension<...>,
    ext1: Extension<...>,
    ext2: Extension<...>,
    ext3: Extension<...>,
    ext4: Extension<...>,
    ext5: Extension<...>,
    ext6: Extension<...>,
    ext7: Extension<...>,
) -> Result<output::Output, error::Error> { ... }
```

Signed-off-by: Daniele Ahmed <[email protected]>
Co-authored-by: Harry Barber <[email protected]>

* Tidy up PR template slightly (#2006)

* Re-export service from root of the generated crate (#2010)

* Always write required query param keys, even if value is unset or empty (#1973)

* fix: issues when sending multipart-upload-related operations with no upload ID by making upload ID required
add: Mandatory trait
add: S3 customization to make Upload Ids mandatory

* add: CHANGELOG.next.toml entry

* update: strategy for tagging shapes as mandatory

* remove: Mandatory trait
undo: S3Decorator changes
add: codegen to generate "else" branch after required query string serialization `if let Some` check
add: missing tokio feature to `TokioTest`

* update: mandatory-query-param.rs tests

* format: run cargo fmt

* update: return BuildError for missing required query params
update: required-query-params.rs tests

* formatting: make RustWriter.paramList more readable
fix: code broken by merge from `main`

* fix: handling of enum strings

* add: codegen tests for required query params
update: required enum query param codegen
add: smithy-rs CHANGELOG.next.toml entry

* fix: presigning.rs test

* fix: use `toType` instead of `asType`
fix: indentation in test

* Detect if the `uniqueItems` trait is used (#2001)

And act like in the rest of the unsupported constraint traits cases.

I missed this in the implementation of #1342.

* fix: missing Tokio feature in S3 integration test (#2017)

fix: remove extra "the" in CDK readme

* Collect extractors into `request` module (#2008)

* Add const to generated enum values() (#2011)

* Add const to generated enum values()

Enum values() functions return static arrays of static strings and could
be of compile time use. Make callable in const contexts.

* Add reference to PR in changelog next for const enum values()

* Correct changelog next target to all for const enum values()

Co-authored-by: Luca Palmieri <[email protected]>

* Improve code generated documentation (#2019)

Co-authored-by: Luca Palmieri <[email protected]>

* Implement `@pattern` on `string` shapes (#1998)

* Refactor `ConstrainedStringGenerator` to extract length checking

* Extract `TryFrom` rendering into its own function

* Extract `ConstraintViolation` definition to separate function

* Add pattern check mock for `@pattern` trait

* Add `regex` to list of server dependencies

* Use real regex check in `check_pattern`

* Add `@pattern` to traits that make `string` shapes directly constrained

* Remove unsupported validation for `@pattern` on strings

* Fix test cases in `ConstraintsTest`

* Remove test disallowing `@pattern` on strings

* Add first test case for `@pattern` on strings

* Fix codegen in `ConstraintViolation::as_validation_exception_field`

* Use `OnceCell` to store `@pattern`'s regex

* Tidy up `ConstrainedStringGenerator` to work with many constraints

* Rename `handleTrait` -> `fromTrait` and make it a static method

* Add docs for `ConstraintViolation` variants

* Fix unescaped slashes in regexes

* Quick hack to get tests compiling

* Fix `sensitive_string_display_implementation` test

* Use new fn name: `asType` -> `toType`

* Refactor `ConstrainedStringGenerator` to not pass in `constraints`

* Add `@pattern` test

* Use `Lazy` instead of `OnceCell`

* Store `&'static str` in `Pattern` error instead of `String`

* Move `TraitInfo` to the bottom

* Extract branches in `TraitInfo.fromTrait` into separate functions

* Add `PatternTrait.validationErrorMessage`

* Add more tests for `@pattern`

* Add entry to `CHANGELOG.next.toml`

* Fix a `@length` + `@pattern` test case

* Remove unused binding in `ConstrainedStringGeneratorTest`

* Remove calls to `trimIndent`

* Use raw Rust strings instead of `escapedPattern`

* Require `regex 1.5.5` instead of `1.7.0`

* Use `Writable`s in `TraitInfo`

* Use single `rust` call for `impl $name` block

* Move `supportedStringConstraintTraits` to `Constraints.kt`

* Fix error message mentioning wrong `ignoreUnsupportedConstraintTraits` key

* Fix usage of `:W` in `rustTemplate` and raw Rust strings

* Use shorthand form for `PatternTrait.validationErrorMessage`

* Fix protocol tests by adjusting the validation message

* Add uses of `@pattern` in `constraints.smithy` model

* Fix broken `RestJsonMalformedPatternReDOSString` test

* Move `TraitInfo` to its own module and separate string-specific details

* Update codegen-core/common-test-models/constraints.smithy

Co-authored-by: david-perez <[email protected]>

* Fix nits in `constraints.smithy`

* Add license to `TraitInfo.kt`

* Make `StringTraitInfo.fromTrait` not return a nullable

* Remove overzealous formatting

* Add some padding to json in `fixRestJsonMalformedPatternReDOSString`

* Add `compile_regex` function for `@pattern` strings

* Add helpful error message when regexes fail to compile

* Add missing coverage in `constraints.smithy`

* Fix examples in `constraints.smithy`

* Fix failing test

* Combine writables in `ConstrainedStringGenerator`

* Fix uses of `Writable.join`

* Use `expect` over `unwrap_or_else`

* Use `once_cell::sync::Lazy` over `once_cell::sync::OnceCell`

Co-authored-by: david-perez <[email protected]>

* Overhaul RustModule system to support nested modules (#1992)

* Overhaul RustModule system to support nested modules

* Cleanups / CR feedback

* Get server unit tests passing

* Fix compilation

* Remove unused import

* CR Feedback II

* customize isn't actually a reserved word—move it to the reserved member section

* Improve service documentation (#2020)

Co-authored-by: Luca Palmieri <[email protected]>

* Add module documentation to `plugin.rs`.

* Remove reference to `FromRequest` in `FromParts`.

* Improve `Route` documentation.

* @range implementation for integer shapes (#2005)

* Draft of @range implementation for integer shapes

* Green tests.

* Fix serialization of constrained integers.

* Fix tagger to include Integer simple shapes.
Fix de/serialization codegen.

* Add range trait to the entry about constraint traits in our changelog.

* Bind a range-constrained integer to various HTTP parts to test our implementation.

* Rename ConstrainedIntGeneratorTest to ConstrainedIntegerGeneratorTest for consistency.

* Remove AsRef implementation.

* Fix constraints models.

* Fix conversion.

* Use ReturnSymbolToParse to dry up.

* Fix builder when constrained types should not be exposed.

* Add customisation to unwrap constrained integers.

* Getting closer - collections need to be handled differently to make everything fall into place.

* Refactor `renderHeaders`

* Fix constraints test.

* Fix ebs.

* Rename for clarity.

* Remove unnecessary From implementation.

* Rename `Size` variant to `Range`.

* Remove stray comments.

* Rename for clarity

* Rename for consistency

* Add test to guard against types for which we do not support range yet

* DRY up branches and the relevant tests.

* Fix header name.

* Fix serialization bug for default values in headers.

* Fix serialization issue for primitive headers.

* Remove SetOfRangeInteger

* Fix pubcrateconstrained unit test.

* Remove duplication

* Revert "Remove SetOfRangeInteger"

This reverts commit f37a560bd0233ad65512c4916292f0f7f8c571e1.

* Reintroduce `SetOfRangeInteger`, but keep them commented out.

* Ignore leading whitespace.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt

Co-authored-by: david-perez <[email protected]>

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt

Co-authored-by: david-perez <[email protected]>

* Update codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt

Co-authored-by: david-perez <[email protected]>

* Update codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedIntegerGeneratorTest.kt

Co-authored-by: david-perez <[email protected]>

* Unify with a single rustTemplate invocation.

* Rename `Type` to `NumberType`

* Update codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt

Co-authored-by: david-perez <[email protected]>

* Formatting issue.

* Move and rename test helper.

* Dry up the logic for default serialization.

* Ooops, I swapped the two branches.

* Add a wrapper block

* Fix support detection.

* Fix CHANGELOG.

* Add (failing) protocol tests for @range on byte/integer/long.

* Fix validation message.
Fix test definitions.

* Mark some tests as expected to fail.
Rename service.

* Use ValueExpression everywhere for more granular control of the ownership component.

* Use the new enhanced module facilities

* Fixes.

* Fix formatting

* Remove unnecessary when.

* Update codegen-core/common-test-models/constraints.smithy

Co-authored-by: david-perez <[email protected]>

* Remove unused shapes.

* Avoid mixing concerns in our test shapes for integer constraints.

* Reuse the trait info machinery

* Update stale comment

* Fix unused attribute.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt

Co-authored-by: Zelda Hessler <[email protected]>

* Avoid unnecessary bindings by manipulating the serialization context directly in the customisations.

* Avoid unnecessary bindings in header serialization by introducing and manipulating the header value serialization context directly in the customisations.

* Add code examples.

* Move `safeName` call into the if branch.

Co-authored-by: david-perez <[email protected]>
Co-authored-by: Zelda Hessler <[email protected]>

* Fix Canary (#2016)

* add: "release-2022-10-26" to canary runner PINNED_SMITHY_RS_VERSIONS
* update: canary-runner README.md

* Implement @range on short (#2034)

* Extract a ConstrainedNumberGenerator from ConstrainedIntegerGenerator

* Update tests' expectations to assume that we support @range for short

* Add ShortShape to all the key places to get @range support

* Generalize generator tests.

* Add tests for the @range trait on short to our constraints.smithy model.

* Update changelog

* Fix type in constraints.smithy

* Introduce an `aws-lambda` feature. (#2035)

* Introduce an `aws-lambda` feature.

* Add CHANGELOG

* Use the new and shiny feature syntax to avoid creating an implicit `lambda_http` feature.

* Add `aws-lambda` feature to the Python server. Enable the `aws-lambda` feature in our example. Be explicit in providing an implementation of request rejection for Box<dyn Error>.

* Add an `aws-lambda` feature flag to the generated Python-specific crate. We enable it by default to make sure the Lambda method ends up visible in the final Python wrapper library.

* Implement @range on long and byte shapes (#2036)

* Group all override-related test cases together.

* Add @range examples for both byte and long in constraints.smithy.

* Implement @range for long and byte shapes.

* Update CHANGELOG

* Fix unit tests.

* Address comments

* Dry up further.

* Upgrade cargo tools (#2032)

* Upgrade `cargo-check-external-types` to 0.1.6
* Upgrade `cargo-deny` to 0.13.5
* Upgrade `cargo-udeps` to 0.1.35
* Upgrade `cargo-hack` to 0.5.23
* Upgrade `cargo-minimal-versions` to 0.1.8

* `CODEOWNERS`: Move Python related folders to `@awslabs/smithy-rs-python-server` team (#2033)

* Move Python related folders to `@awslabs/smithy-rs-python-server` team

* Add `@awslabs/smithy-rs-server` team to Python related folders

Co-authored-by: Zelda Hessler <[email protected]>

* Restore `into_make_service_with_connect_info` module (#2039)

* RFC: RequestId for services (#1942)

* RFC 24 RequestId

Signed-off-by: Daniele Ahmed <[email protected]>

* Add lambda extractors (#2038)

* Implement `@length` on collections (#2028)

* Refactor `ConstrainedMapGenerator` slightly

* Add `@length` list operation in `constraints.smithy`

* Add more setup required for rendering constraint `list`s

* Add initial draft of `ConstrainedCollectionGenerator`

* Add license header to `LengthTraitCommon`

* Add draft of collection constraint violation generation

Copy `MapCostraintViolationGenerator` into
`CollectionConstraintViolationGenerator` for now.

* Add `Visibility.toRustQualifier`

* Implement `CollectionConstraintViolationGenerator`

* Use `TraitInfo` on `CollectionConstraintViolationGenerator`

* Update docs on `ConstrainedCollectionGenerator`

* Improve formatting on rust code

* Don't generate `ConstraintViolation` enum twice

* Make symbol provider work with constrained lists

* Fix call to `ConstraintViolation::Member`

* Render constraint validation functions for lists

* Fix call to `usize::to_string`

* Use map json customisation for collections as well

* Update to use overhauled module system

* Add constraint traits check for collection shapes

* Remove test checking that `@length` is not used in `list`s

* Refactor use of `shape.isDirectlyConstrained`

* Fix failing `UnconstrainedCollectionGeneratorTest` test

* Fix test

* Actually fix the test

* Update changelog

* Fix `constrainedCollectionCheck`

* Add tests for `ConstrainedCollectionGenerator`

* Make tests work for when sets are implemented

* Remove mention of `set` in changelog

Co-authored-by: david-perez <[email protected]>

* Fix styling in `constraints.smithy`

Co-authored-by: david-perez <[email protected]>

* Use `check` instead of `assert`

* Grammar fix in comment about `Option<Box<_>>`

* Rename unsupported length data class for blobs

- `UnsupportedLengthTraitOnCollectionOrOnBlobShape` -> `UnsupportedLengthTraitOnBlobShape`

* Add TODO about `uniqueItems` not being implemented yet

* Change error message: `unconstrained` -> `not constrained`

* Add imports to fix docs

* Remove unused `modelsModuleWriter` parameter

* Use `filterIsInstance` and coalesce `filter + map`

* Add `check` in json customization

* Use `Set` instead of `List` for supported contraints

* Use `toMutableList` to avoid creating an extra list

* Add `@length` list to `ConA`

* Add `@httpQuery`-bound `@length` list example

* Add `@httpHeader`-bound `@length` list

* Fix `UnconstrainedCollectionGeneratorTest` test

* Fix rendering of constrained lists as header values

* Split very long line

* Add docs for `ConstraintViolation::Member` for lists

* Pass `length` variable name to `LengthTrait.rustCondition`

* Refactor long conditional

* Homogenise conditional

Co-authored-by: david-perez <[email protected]>

* Improve extractor errors (#2041)

Co-authored-by: Luca Palmieri <[email protected]>

* Document the new service builder (#2021)

* Document the new service builder

Signed-off-by: Daniele Ahmed <[email protected]>

* The TODO comment should not be visible in the public docs. (#2045)

* Add server SDK constraint traits RFC (#1199)

* Add Endpoint Resolver Implementation (#2030)

* Add Endpoint Resolver Implementation

This commit adds `EndpointDecorator`, standard libary + tests that can be used to add endpoints 2.0 to the AWS SDK.

It _does not_ actually wire these things up. We'll follow up with a PR that actually integrates everything.

* CR Feedback

* CR feedback II

* Add default for the Smithy test workspace (#1981)

* Add default for the Smithy test workspace

* CR feedback

* Upgrade test SDK models to IDLv2 (#2049)

* Various small corrections to server documentation (#2050)

* Link to super from Handler and OperationService

* Note that structure documentation is from model

* Don't refer to ZSTs in operation_shape.rs opener

* Re-export everything from service to root

* Add reference to root documentation on service

* Fix spelling of MissingOperationsError

* #[doc(hidden)] for opaque_future

* Rename from-parts.md to from_parts.md

* Fix Connected link

* Use S type parameter and service as variable name

* Remove MissingOperation dead code

* Remove Operation header from operation.rs

* Remove reference to closures

* Document ConnectInfo<T> .0

* Add BoxBody documentation

* Rephrase operation.rs documentation

* Reference PluginPipeline from PluginStack

* Move the BoxBody documentation

* Document Plugin associated types

* Add example implementation for Plugin

* Link to plugin module from Plugin

* Improve FromParts/FromRequest documentation

* Remove links to doc(hidden) RuntimeError

* Add link from Upgradable to operation module

* Fix service builder docs (#2046)

* Fix doc prefix in service generator

* Move documentation to root module

* Documentation improvements

* Expose handler imports

Signed-off-by: Daniele Ahmed <[email protected]>
Co-authored-by: Harry Barber <[email protected]>

* Unhide new service builder and deprecate the prior (#1886)

Co-authored-by: david-perez <[email protected]>

* Normalize URI paths during signing (except for S3) (#2018)

* De-dupe multiple leading forward slashes in request

This commit fixes a bug reported in
https://github.com/awslabs/aws-sdk-rust/issues/661.
Note that this de-duping is not part of URI path normalization because it
must happen to a request for any service, including S3.

* Normalize URI paths for canonical requests

This commit enables URI path normalization in aws-sigv4. It follows
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
to create the canonical URI that is part of the canonical request.

* Avoid using OS specific path separator

This commit updates the implementation of normalize_path_segment so that it
does not use an OS specific path separator, specifically "\\" on Windows and
always uses a forward slash "/". It addresses a test failure on Windows:
https://github.com/awslabs/smithy-rs/actions/runs/3518627877/jobs/5897764889.

* Fix the rustdoc::bare_urls warning from cargo doc

* Fix a typo Normalzie -> Normalize

* Limit de-duping multiple leading forward slashes to S3

This commit addresses https://github.com/awslabs/smithy-rs/pull/2018#issuecomment-1325283254.
There is a case  where an empty string between leading double forward
slashes is meaningful so blindly de-duping them breaks the use case.
Therefore, handling multiple leading forward slashes in this manner
should be applied only to S3.

* Add an integration test per https://github.com/awslabs/aws-sdk-rust/issues/661

This commit adds an integration test verifying the special rule for URI
path normalization applies to S3. That is, leading forward slashes should
be de-deduped and normalizing URI paths as prescribed in RFC 3986 should
be ignored.

* Update CHANGELOG.next.toml

* Revert 87b749c

This commit reverts 87b749c, the reason being that we need to better
understand the requirement for de-duplicating leading forward slashes
in requests, i.e. do we even need to do it or if so where should we
perform the de-dupe operation?

* Revert naming of UriPathNormalizationMode variants

This commit reverts the naming of variants in UriPathNormalizationMode.
They were PerRfc3986 and ForS3, but they should be more generic like
Enabled and Disabled as we had before.

* Update CHANGELOG.next.toml

* Simplify integration test using capture_request

This commit simplifies the integration test normalize-uri-path for s3.
Previously, the test used ReplayingConnection but it was an overkill
to test something as simple as whether the generated request contained
the expected URI and signature.

* Update aws/rust-runtime/aws-sigv4/src/http_request/settings.rs

Co-authored-by: John DiSanti <[email protected]>

* Update aws/rust-runtime/aws-sigv4/src/http_request/settings.rs

Co-authored-by: John DiSanti <[email protected]>

* Update aws/rust-runtime/aws-sigv4/src/http_request/uri_path_normalization.rs

Co-authored-by: John DiSanti <[email protected]>

* Address review feedback on normalize_path_segment

This commit addresses the following code review feedback:
https://github.com/awslabs/smithy-rs/pull/2018#discussion_r1034192229
https://github.com/awslabs/smithy-rs/pull/2018#discussion_r1034194621
https://github.com/awslabs/smithy-rs/pull/2018#discussion_r1034195625

* Update CHANGELOG.next.toml

This commit addresses https://github.com/awslabs/smithy-rs/pull/2018#discussion_r1034198861

* Preallocate the Vec capacity for normalize_path_segment

This commit addresses https://github.com/awslabs/smithy-rs/pull/2018#discussion_r1034195625.
It counts the number of slashes to preallocate the capacity of a Vec used
in normalize_path_segment.

* Address https://github.com/awslabs/smithy-rs/pull/2018\#discussion_r1034188849

* Update aws/rust-runtime/aws-sigv4/src/http_request/settings.rs

Co-authored-by: John DiSanti <[email protected]>

* Update aws/rust-runtime/aws-sigv4/src/http_request/settings.rs

Co-authored-by: John DiSanti <[email protected]>

* Update aws/rust-runtime/aws-sigv4/src/http_request/settings.rs

Co-authored-by: John DiSanti <[email protected]>

Co-authored-by: Yuki Saito <[email protected]>
Co-authored-by: John DiSanti <[email protected]>

* Fix inability to create service clients when `default-features = false` (#2055)

* add: tests for config/client construction when default features are disabled
fix: enable users to create service clients when default features are disabled
update: missing HTTP connector panic messages

* Apply suggestions from code review
Co-authored-by: John DiSanti <[email protected]>

* Fix server book documentation links (#2059)

* Respect the sensitive trait on container shapes (#1983)

* Make enum forward-compatible

This commit implements the suggested approach described in
https://github.com/awslabs/smithy-rs/issues/627#issuecomment-995923092

The idea is that once the user writes a match expression against an enum
and assumes that an execution path comes to a particular match arm, we
should guarantee that when the user upgrades a version of SDK, the
execution path should come to the same match arm as before.

* Add unit test to ensure enums are forward-compatible

The test first mimics the user's interaction with the enum generated from
a model where the user writes a match expression on the enum, wishing to
hit the match arm corresponding to Variant3, which is not yet supported
by the model. The test then simulates a scenario where the user now has
access to the updated enum generated from the next version of the model
that does support Variant3.
The user's code should compile in both of the use cases.

* Generate rustdoc for enum's forward-compatibility

This commits generates rustdoc explaining to the users how they might
write a match expression against a generated enum so their code is
forward-compatible. While it is explained in
https://github.com/awslabs/smithy-rs/issues/627#issuecomment-995923092,
it can be helpful if the users can read it in rustdoc right below where
the enum's definition is shown.

* Make snippet in rustdoc text

This commit updates a rustdoc code snippet generated for an enum from
`rust,no_run` to `text`. We observed that the snippet fails to compile
in CI because the name of the enum was not fully qualified; code in
rustdoc is treated in a similar way that code in integration tests is
treated as being outside of a crate, but not part of the crate, which
means the name of the crate needs to be spelled out for a `use` statement
if we want to import the enum to the code in rustdoc. However, the name
of the crate is usually one-off, generated on the fly for testing, making
it not obvious to hard-code it or obtain it programmatically from within
Kotlin code.

* Suppress missing doc lint for UnknownVariantValue

This commit marks UnknownVariantValue as `#[allow(missing_docs)]` because
failing to do so will cause tests in CI to fail.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: Russell Cohen <[email protected]>

* Generate UnknownVariantValue via forInlineFun

This commit attempts to create UnknownVariantValue using
RuntimeType.forInlineFun. Prior to this commit, it was generated per enum
but that caused multiple definitions of UnknownVariantValue in a single
file as there can be multiple enums in the file.
By using RuntimeType.forInlineFun, we can generate a single UnknownVariantValue
per module and the enums within the module can refer to the same instance.

This commit, however, fails to pass the unit tests in EnumGeneratorTest.
The issue seems to be that a RustWriter used in the tests does not end up
calling the finalize method, failing to render inline functions.

* Replace "target == CodegenTarget.CLIENT" with a helper

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1014390811
but uses an existing helper CodegenTarget.renderUnknownVariant.

* Update EnumGeneratorTests to use TestWorkspace

This commit addresses failures for unit tests in EnumGeneratorTests.
Now that we use RuntimeType.forInlineFcn in EnumGenerator, we need
to ensure that the inline functions should be rendered to a Rust
source file during testing. RustWriter, used in EnumGeneratorTests
prior to this commit, was not capable of doing so. We thus update
EnumGeneratorTests to use TestWorkspace by which we ensure that the
inline functions are rendered during testing.

* Make sure to use the passed-in variable for shapeId

This commit fixes a copy-and-paste error introduced in 712c983. The
function should use the passed-in variable rather than the hard-coded
literal "test#SomeEnum".

* Address https://github.com/awslabs/smithy-rs/pull/1945\#discussion_r1014389852

* Update CHANGELOG.next.toml

* Update CHANGELOG.next.toml

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1017235315

* Avoid potential name collisions by UnknownVariantValue

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1017237745.
It changes the module in which the `UnknownVariantValue` is rendered.
Before the commit, it was emitted to the `model` module but that might cause
name collisions in the future when a Smithy model has a shape named
`UnknownVariantValue`. After this commit, we render it to the `types` module.

* Move re-exports from lib.rs to types.rs

This commit moves re-export statements in client crates from `lib.rs` to
`types.rs`. The motivation is that now that we render the struct
`UnknownVariantValue` in a separate file for the `types` module, we can
no longer have a `pub mod types {...}` block in `lib.rs` as it cannot
coexist with `pub mod types;`.

* Add docs on UnknownVariantValue

This commit adds public docs on `UnknownVariantValue`. Since it is
rendered in `types.rs`, the users may not find the `Unknown` variant
of an enum in the same file and may wonder what this struct is for when
seeing it in isolation.

* Update CHANGELOG.next.toml

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1020414459

* Update the module documentation for types

This commit updates rustdoc for the types module to reflect that fact
that the module now contains not only re-exports but also an auxiliary
struct `UnknownVariantValue`.

* Add extensions to run code block depending on the target

This commit introduces extensions on CodegenTarget that execute the block
of code depending on the target is for client or for server. These
extensions are intended to replace if-else expressions throughout the
codebase explicitly checking whether the target is for client or for server.
We do not update such if-else expressions all at once in this commit.
Rather, we are planning to make incremental changes to update them
opportunistically.

* Respect the sensitive trait on enums

This commit fixes #1745. It allows enums to respect the sensitive trait.
When annotated as such, an enum will not display its data and instead
will show the redacted text upon debug print.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: John DiSanti <[email protected]>

* Move extensions into CodegenTarget as methods

This commit addresses https://github.com/awslabs/smithy-rs/pull/1945#discussion_r1021952411.
By moving extensions into the CodegenTarget enum, no separate import is
required to use them.

* Update codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGeneratorTest.kt

Co-authored-by: david-perez <[email protected]>

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: david-perez <[email protected]>

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt

Co-authored-by: david-perez <[email protected]>

* Update CHANGELOG.next.toml

* Configure the (in|ex)clusion of the Debug trait for containers per members' sensitive trait (#2029)

* Removes Debug for shape with sensitive trait

This commit centralizes a place where the codegen excludes the Debug
trait if a shape has the sensitive trait. Previously the exclusion was
handled locally in each shape, e.g. StructureGenerator and EnumGenerator.
However, that approach may overlook a certain shape we also need to treat
as such. We now handle the exclusion of the Debug trait in one place,
BaseSymbolMetadataProvider.

* Stop excluding the Debug trait locally

This commit updates EnumGenerator and StructureGenerator based on the
change made to BaseSymbolMetadataProvider in the previous commit.
Now that the exclusion of the Debug trait was centralized, those
classes in question no longer need to do so individually.

* Implement a custom Debug trait in BuilderGenerator

This commit implements a custom Debug trait in BuilderGenerator now that
the derived Debug trait is excluded from BaseSymbolMetadataProvider for
the structure container. The implementation of the custom Debug trait
pretty much follows that of StructureGenerator.

* Implement a custom Debug trait in UnionGenerator

This commit implements a custom Debug trait in BuilderGenerator now that
the derived Debug trait is excluded from BaseSymbolMetadataProvider for
the union container. The implementation of the custom Debug trait pretty
much follows that of EnumGenerator.

* Implement a custom Debug trait in ServerBuilderGenerator

This commit implements a custom Debug trait in ServerBuilderGenerator now
that the derived Debug trait is excluded from BaseSymbolMetadataProvider
for the structure container. The implementation of the custom Debug trait
pretty much follows that of StructureGenerator.

* Add the Copyright header

* Update CHANGELOG.next.toml

* Update Debug impl for UnionGenerator

This commit updates the implementation of a custom Debug trait impl for
UnionGenerator. Turns out that in a Union, a member target can be marked
as sensitive separately outside the Union. Therefore, the implementation
of a custom Debug trait has two cases depending on where the sensitive
trait appears, either it is applied to the whole Union or to a member
target.

* Peek at member sensitivity for Debug trait (in|ex)clusion

This commit addresses https://github.com/awslabs/smithy-rs/pull/2029#discussion_r1034205685.
With this change, structure shapes no longer need to exclude the Debug
trait unconditionally. The upshot is that we may be able to avoid a
custom Debug impl for a structure where the derived Debug will do, i.e.
when there is no sensitive trait either at a container level or at a
member level.

* Remove statement that does not seem to take effect

This commit addresses https://github.com/awslabs/smithy-rs/pull/2029#discussion_r1036288146

* Rename renderDebugImplForUnion -> renderFullyRedactedDebugImpl

This commit addresses https://github.com/awslabs/smithy-rs/pull/2029#discussion_r1036290722

* Rename renderDebugImplForUnionMemberWise -> renderDebugImpl

This commit addresses https://github.com/awslabs/smithy-rs/pull/2029#discussion_r1036291209

Co-authored-by: Yuki Saito <[email protected]>

Co-authored-by: Saito <[email protected]>
Co-authored-by: Russell Cohen <[email protected]>
Co-authored-by: Yuki Saito <[email protected]>
Co-authored-by: John DiSanti <[email protected]>
Co-authored-by: david-perez <[email protected]>

* Tracing output json (#2060)

* Tracing output json

Signed-off-by: Daniele Ahmed <[email protected]>

* Remove client body callbacks (#2065)

* Implement FromParts for Option, Result (#2068)

* Implement FromParts for Option, Result

Signed-off-by: Daniele Ahmed <[email protected]>

* Endpoints 2.0 Integration pre-work (#2063)

* Split endpoint resolution middleware into two parts & refactor endpoint generation

* Endpoints 2.0 Integration pre-work

This PR does a 3 bits of pre-work ahead of ep2 integration:
1. Split endpoint resolution into two separate middlewares:
  1. A smithy native middleware that applies URI and headers
  2. An AWS middleware that applies the auth schemes
2. Add vendorParams support to the ProtocolTestGenerator so that protocol tests can insert a region.
3. Simplify endpoint resolution logic by allowing `make_operation` to fail when an endpoint cannot be resolved.

* Back out previous change to insert endpoint directly into the bag

* backout changes to property bag

* Update changelog & add more docs

* Fix AWS test

* Fix test

* allow parse_url functions to be unused (#2071)

* Establish default max idle connections on default connectors (#2064)

* Render a Union member of type Unit to an enum variant without inner data (#1989)

* Avoid explicitly emitting Unit type within Union

This commit addresses #1546. Previously, the Unit type in a Union was
rendered as an enum variant whose inner data was crate::model::Unit.
The way such a variant appears in Rust code feels a bit odd as it
usually does not carry inner data for `()`. We now render a Union
member of type Unit to an enum variant without inner data.

* Address test failures washed out in CI

This commit updates places that need to be aware of the Unit type attached
to a Union member. Those places will render the Union member in a way that
the generated Rust code does not include inner data of type Unit. It should
help pass `codegen-client-test:test` and `codegen-server-test:test`.

Further refactoring is required because each place we updated performs an
explicit if-else check for special casing, which we should avoid.

* Update codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGeneratorTest.kt

Co-authored-by: John DiSanti <[email protected]>

* Remove commented-out code

* Add a helper for comparing against ShapeId for Unit

This commit adds a helper for checking whether MemberShape targets the
ShapeId of the Unit type. The benefit of the helper is that each call
site does not have to construct a ShapeId for the Unit type every time
comparison is made.

* Move Unit type bifurcation logic to jsonObjectWriter

This commit moves the if-else expression hard-coded in the StructureShape
matching case within RustWriter.serializeMemberValue to jsonObjectWriter.
The previous approach of inlining the if-else expression out of
jsonObjectWriter to the StructureShape case and tailoring it to the call
site violated the DRY principle.

* Make QuerySerializerGenerator in sync with the change

This commit brings the Unit type related change we've made so far to
QuerySerializerGenerator. All tests in CI passed even without this
commit, but noticing how similar QuerySerializerGenerator is to
JsonSerializerGenerator, we should make the former in sync with the
latter.

* Update CHANGELOG.next.toml

* Refactor ofTypeUnit -> isTargetUnit

This commit addresses https://github.com/awslabs/smithy-rs/pull/1989#discussion_r1035372417.
The extension should be renamed to isTargetUnit and moved to Smithy.kt.

* Update codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt

Co-authored-by: John DiSanti <[email protected]>

* Simplify if-else in jsonObjectWriter

This commit addresses https://github.com/awslabs/smithy-rs/pull/1989#discussion_r1037651893

* Avoid the union member's reference name being empty

This commit addresses https://github.com/awslabs/smithy-rs/pull/1989#discussion_r1037654601
Even if member's reference name "inner" is present, it will not be an
issue when the member is the Unit type where the reference name "inner"
cannot be extracted. The reason is jsonObjectWriter won't render the
serialization function if the member is the Unit type.

That said, the same change in QuerySerializerGenerator may not be the
case so we'll run the tests in CI and see what breaks.

* Ensure Union with Unit target can be serialized

This commit updates serialization of a Union with a Unit target in
different types of serializers. We first updated protocol tests by
adding a new field of type Unit and some of them failed as expected.
We worked our way back from those failed tests and fixed the said
implementation for serialization accordingly.

* Ensure Union with Unit target can be parsed

This commit handles deserialization of a Union with a Unit target in
XmlBindingTraitParserGenerator. Prior to the commit, we already handled
the said deserialization correctly in JsonParserGenerator but missed it
in XmlBindingTraitParserGenerator. We added a new field of type Unit to
a Union in parser protocols tests, ran them, and worked our way back from
the failed tests.

* Ensure match arm for Unit works in custom Debug impl

This commit handles a use case that came up as a result of combining two
updates, implementing a custom Debug impl for a Union and avoid rendering
the Unit type in a Union. In the custom Debug impl, we now make sure that
if the target is of type Unit, we render the corresponding match arm
without the inner data. We add a new unit test in UnionGeneratorTest.

* Fix unused variables warnings in CI

This commit adds the #[allow(unused_variables)] annotation to a block of code
generated for a member being the Unit type. The annotation is put before we
call the handleOptional function so that it covers the whole block that the
function generates.

* Fix E0658 on unused_variables

This commit fixes an error where attributes on expressions are experimental.
It does so by rearranging code in a way that achieves the same effect but
without #[allow(unused_variables)] on an expression.

Co-authored-by: Yuki Saito <[email protected]>
Co-authored-by: John DiSanti <[email protected]>

* Improve client tracing spans (#2044)

* Emit spans for implementers of map request middleware traits
* Instrument dispatch with its own span
* Fix trace span hierarchy
* Partially flatten the middleware span hierarchy
* Make `MapRequest::name` required
* Add sub-spans to the `load_response` span

* Add `tracing` events to signing an…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfc Marks a PR as an RFC server Rust server SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants