* Add support for missing authorizer members
This commit adds support for the `authorizerPayloadFormatVersion` and
`enableSimpleResponses` members in `apiGateway#authorizers`.
These members are used to properly define authorizers for HTTP APIs.
This commit updates Smithy IDL v2 to support custom default values
rather than only default zero values.
We didn't previously support custom default values because we wanted to
be able to change default values if ever needed, we wanted to avoid
information disclosure of unreleased internal members, and we wanted
clients to be able to fill in a default zero value if a required member
is missing on the wire (implying that the member transitioned from
required to default).
While default zero values are a big simplification for clients, they do
come with awkward tradeoffs. For example, the zero value of an enum was
`""` (or it could have been the first enum in the shape definition but
that's problematic due to model filtering, and it requires a lot of
up-front planning from service teams). We also observed that there are
likely 1,000+ members already used in AWS that have default values that
are only captured through documentation. If we could actually model
defaults, it would be easier to catch changes to default values in
automated diff tools and discuss them in API reviews.
To address these concerns and support custom default values, we
recommend the following:
* clients implement presence tracking and only serialize default values if
the member is explicitly set to the default value. This allows services
to change defaults if ever necessary.
* servers always serialize default values unless the member is marked with
the `@internal` trait. This makes it explicit as to what the server
thinks the default value is while still preventing unintended
information disclosure of unreleased features.
* To avoid downtime and account for misconfigured servers that do not
serialize required or default members, clients attempt to populate the
member with a default zero value.
More details and specifics can be found in
defaults-and-model-evolution.md.
Enum changes:
* Given the addition of syntax sugar for assigning values to
defaulted structure members, this commit also allows syntactic sugar for
assigning values to enum and intEnum shapes without the use of the
`@enumValue` trait.
* Now that there is no default zero value for enums, the `@enumDefault`
trait has been removed.
This commit updates traits to use the originally provided node value in
a model file so that the value is persisted as-is when calling toNode on
a trait. This prevents needing to duplicate the work of creating the
trait and helps expose issues like typos in traits because invalid
properties can be detected when validating traits against the model.
The tradeoff of this change is that if a trait does any kind of
normalization of values or setting of defaults or if a trait has logic
around omitting empty lists, false, etc, then traits may need to
override equals and hashCode since relying on toNode equality would
render two traits inequal. That generally is fine, but it may be a
problem if a model transformation is performed and you need to compare
two models, you need to merge two models that migth contain duplicate
shapes as a result of a transform, etc.
This commit introduces the `smithy.api#Unit` type that that is used for
operations with no meaningful input or output, and for tagged union
members with no meaningful value. Operations will now, by default,
target `smithy.api#Union` if they do not define input or output.
This commit also introduces the `@input` and `@output` traits that are
used to specialize structures as for use only as the input or output of
a single operation. The `@input` trait provides structures with more
relaxed backward compatibility constraints so that, for example, when
things like the `@default` value trait are added, it is considered a
backward compatible change to remove the `@required` trait from the
member of a structure marked with the `@input` trait.
New methods were added to smithy-model to account for the fact that
operations always now have input and output shapes. The existing methods
that return Optional shape IDs and structures remain but are marked as
deprecated. Any usage of these deprecated methods were updated
throughout the monorepo to use the newly introduced methods (this
accounts for a lot of the churn).
If an operation defines no input or output, a WARNING is emitted. If an
operation defines input or output other than `smithy.api#Unit` and the
targeted shapes aren't marked with `@input` or `@output` a validation
event is emitted. This also accounts for a lot of churn in test cases
(either to add suppressions or to add input and output). Due to the
suppressed events, Smithy's errorfiles test runner was updated to no
longer require that SUPPRESSED validation events are explicitly listed
in errorfiles (they can be but don't have to be).
Along with updating every operation to define input and output shapes,
this change also removes many JSON AST examples. These examples were
unnecessary and a liability for the team to maintain. The appropriate
place to document how the IDL and JSON AST interact is in the
specification language around the IDL and JSON AST, not spread across
the entire specification. Alternatively, I could have edited each
impacted AST example, but I worried that I would add mistakes and they
are not validated against the IDL examples.
This adds a warning-level validation event for enums that do not use
the name property. It additionally updates all built in enums and
enums used for tests where the absense of the name isn't deliberate.
Unfortunately, Kotlin isn't well-supported in VS Code, and we're looking
at adding VS plugins to Smithy in the future. We've also found that the
majority of Gradle plugins only provide Groovy examples, making it
harder to write out build scripts due to a lack of Kotlin examples. With
IDE plugins and a quicker compile/test feedback loop, the loss in type
safety realized by Kotlin isn't as bad as I thought it would be.
This commit deprecates the old access pattern used to create a
KnowledgeIndex (`model.getKnowledge(X.class)`) in favor of using a new
convention: `X.of(model)`. KnowledgeIndex implementations are now
expected to provide a public static method named `of` that accepts a
`Model` and returns an instance of the class. The instance should be
created by calling `model.getKnowledge(Class<T>, Function<Model, T>)`
which will ensure the index is cached in the given model.
This change is the first step towards seeing if we can get full GraalVM
native image support. It also makes using a KnowledgeIndex more
ergonomic IMO, and remove reflection, which is generally a good thing.
The deprecated method will eventually be removed in a subsequent
release.
We've been using the "internal" tag to indicate that something is
internal only and should not be exposed to external consumers of a model
(i.e., people that don't work at the same company as the service team).
The original motivation for using tags and not traits was that there are
more dimensions to filtering shapes out of models than just internal vs
external. However, the internal/external split is so common that it
makes sense to make it a trait, which is more configurable and less
based on convention. Tags can still be used for filtering things out of
models (for example, an operation could be tagged so that it is only
exposed to a particular customer).
There are other cases where we migrated away from tags and moved to
traits like the `aws.api#controlPlane` and `aws.api#dataPlane` traits.
To support this change, a SmithyBuild transform was added that removes
shapes marked as internal. Note that this does not perform tree shaking
since like other transforms, tree shaking is performed by a dedicated
transform.
Currently, when converting the Smithy model to API Gateway for HTTP APIs, it fails because it doesn't configure the `payloadFormatVersion`. This change adds the payload format version to the IntegrationTrait.
The payload format version specifies the format of the data that API Gateway sends to an integration, and how API Gateway interprets the response. If you create an integration for HTTP APIs programmatically, you **must** specify a payloadFormatVersion. The supported values are 1.0 and 2.0.
The `$version` control statement can now be set to only a major version
(e.g., "1") to indicate that an implementation must support a version >= 1
and < 2. `$version` can now be set to `major.minor` (e.g., "1.1") to
indicate that an implementation must support a version >= 1.1 and < 2.
This commit removes the support for setting annotation traits to true
because the previous rationale for supporting that no longer holds true.
The previous rationale was that supporting `true` makes writing the AST
JSON files easier. However, we've already crossed the point where we
don't want humans writing the JSON AST, and we want it to be as easy to
parse as possible.
The AST format now requires that `{}` is provided for annotation traits.
The IDL no longer supports setting a trait to `true` or `null`, as that
was only supported in order to provide isomorphism with the AST support.
We previously didn't persist which model format a pending trait
definition came from, so it was necessary to give them the same feature
set. However, now the Smithy loader maintains a boolean value that tells
the loader whether or not a value is an "annotation", meaning trait with
no value. This isn't a feature used in the AST loader, but is used in
the IDL loader.
Further, the BooleanTrait abstract class was renamed to AnnotationTrait.
This is something I wanted to do for a long time, but never did since it
would cause so much churn, and the name wasn't that far off from what it
represents. However, now that `true` isn't supported for annotation
traits, the name BooleanTrait makes no sense. This commit renames it and
takes the BC hit because it's now worth it.
This commit updates API Gateway traits to use the NodeMapper and changes
different getter/setter methods to use NodeMapper compatible API names.
Further, support for omitting the toNode implementation of a class was
added so that the NodeMapper can be used inside of a toNode method.
Finally, support for omitting empty arrays and objects was added so the
NodeMapper doesn't inject a bunch of empty values when it isn't
desirable.
CONVERT_TO_TEXT is necessary on OPTIONS request otherwise you may see
500 errors from API Gateway. Further, this commit cleans up several
getters/setters on the IntegrationTrait and MockIntegrationTrait to
match what a code generator would produce and how the NodeMapper works.
This commit updates the @externalDocumentation trait to be a map of
named urls instead of a single url. This provides more context for
the given links and enables the ability to have multiple links per
trait application.
An update to the OpenApi conversion has been made to utilize this
when populating "externalDocs" fields. A configurable, priority
ordered list of url names is available through
`"openapi.externalDocs"`; it defaults to "Homepage", "Api
Reference", "User Guide", "Developer Guide", "Reference", and
"Guide". This list is compared case insensitively. This also
fixes an issue where "externalDocs" fields could be generated as
strings instead of the properly formatted external documentation
object in the resulting OpenApi document.
The enum trait is currently a map of enum value to the enum definition.
This means that models often look like this:
```
@enum(foo: {}, bar: {})
string MyString
```
Ideally, a name and documentation are provided too:
```
@enum(
foo: {name: "FOO", documentation: "foo docs..."},
bar: {name: "BAR", documentation: "bar docs..."})
```
However, the difference between the key and "name" has resulted in
confusion as to which is which. This is caused by a couple things:
1. The key doesn't tell you what it defines. You have to know that the
the key defines the value of the enum to make sense of the trait.
2. The key has to be quoted if it deviates from the allows ABNF for node
object keys. This has also led to confusion.
Although this is a breaking change, using a list of structures where
everything has a name will result in models that are easier to
understand, and hopefully modelers will more often include properties
like `name` and `documentation`.