Remove allowing true for annotation traits

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 is contained in:
Michael Dowling 2020-04-16 16:07:45 -07:00 committed by JordonPhillips
parent 4eb73373bc
commit 3485a2e9cf
186 changed files with 857 additions and 853 deletions

View File

@ -429,7 +429,7 @@ operation within the service.
"type": "service",
"version": "2018-03-17",
"traits": {
"aws.protocols#restJson1": true,
"aws.protocols#restJson1": {},
"aws.auth#sigv4": {
"name": "weather"
},

View File

@ -66,7 +66,7 @@ Trait value
"type": "service",
"version": "2018-03-17",
"traits": {
"aws.protocols#restJson1": true,
"aws.protocols#restJson1": {},
"aws.api#service": {
"sdkId": "Some Value"
},
@ -127,7 +127,7 @@ operation MUST NOT be used as part of the request signature calculation:
"target": "smithy.example#PutThingsOutput"
},
"traits": {
"aws.auth#unsignedPayload": true
"aws.auth#unsignedPayload": {}
}
}
}

View File

@ -773,7 +773,7 @@ plane unless an operation or resource is marked with the
"target": "smithy.example#PutThingsOutput"
},
"traits": {
"aws.api#controlPlane": true
"aws.api#controlPlane": {}
}
}
}
@ -827,7 +827,7 @@ plane unless an operation or resource is marked with the
"target": "smithy.example#PutThingsOutput"
},
"traits": {
"aws.api#dataPlane": true
"aws.api#dataPlane": {}
}
}
}
@ -1111,8 +1111,8 @@ using an ``clientEndpointDiscoveryId``.
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -49,7 +49,7 @@ Value type
"type": "service",
"version": "2020-02-05",
"traits": {
"aws.protocols#ec2Query": true
"aws.protocols#ec2Query": {}
}
}
}

View File

@ -386,7 +386,7 @@ condition key inference disabled.
}
},
"traits": {
"aws.iam#disableConditionKeyInference": true
"aws.iam#disableConditionKeyInference": {}
}
}
}

View File

@ -50,7 +50,7 @@ See
"type": "service",
"version": "2020-02-05",
"traits": {
"aws.protocols#awsJson1_0": true
"aws.protocols#awsJson1_0": {}
}
}
}

View File

@ -50,7 +50,7 @@ See
"type": "service",
"version": "2020-02-05",
"traits": {
"aws.protocols#awsJson1_1": true
"aws.protocols#awsJson1_1": {}
}
}
}

View File

@ -50,7 +50,7 @@ See
"type": "service",
"version": "2020-02-05",
"traits": {
"aws.protocols#awsQuery": true
"aws.protocols#awsQuery": {}
}
}
}

View File

@ -82,7 +82,7 @@ The following example defines a service that uses ``aws.protocols#restJson1``.
"type": "service",
"version": "2020-04-02",
"traits": {
"aws.protocols#restJson1": true
"aws.protocols#restJson1": {}
}
}
}

View File

@ -50,7 +50,7 @@ See
"type": "service",
"version": "2020-02-05",
"traits": {
"aws.protocols#restXml": true
"aws.protocols#restXml": {}
}
}
}

View File

@ -337,8 +337,8 @@ The following example defines two operations:
}
],
"traits": {
"smithy.api#httpBasicAuth": true,
"smithy.api#httpDigestAuth": true,
"smithy.api#httpBasicAuth": {},
"smithy.api#httpDigestAuth": {},
"smithy.api#auth": [
"smithy.api#httpBasicAuth"
]

View File

@ -277,8 +277,8 @@ explicitly on the operation.
"target": "smithy.example#GetFoosOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#collection": true,
"smithy.api#readonly": {},
"smithy.api#collection": {},
"smithy.api#paginated": {
"inputToken": "nextToken",
"outputToken": "nextToken",
@ -307,7 +307,7 @@ explicitly on the operation.
"foos": {
"target": "smithy.example#StringList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -373,8 +373,8 @@ settings from a service.
"target": "smithy.example#GetFoosOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#collection": true,
"smithy.api#readonly": {},
"smithy.api#collection": {},
"smithy.api#paginated": {
"items": "foos"
}
@ -445,7 +445,7 @@ wrapper where the output token and items are referenced by paths.
"target": "smithy.example#GetFoosOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#paginated": {
"inputToken": "nextToken",
"outputToken": "result.nextToken",
@ -471,7 +471,7 @@ wrapper where the output token and items are referenced by paths.
"result": {
"target": "smithy.example#ResultWrapper",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -485,7 +485,7 @@ wrapper where the output token and items are referenced by paths.
"foos": {
"target": "smithy.example#StringList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -222,7 +222,7 @@ contain a valid shape ID that targets an integer shape in the model.
"smithy.example#integerRef": {
"type": "string",
"traits": {
"smithy.api#trait": true,
"smithy.api#trait": {},
"smithy.api#idRef": {
"failWhenMissing": true,
"selector": "integer"
@ -547,7 +547,7 @@ in a response.
"foo": {
"target": "smithy.example#FooString",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -591,7 +591,7 @@ Value type
"target": "smithy.api#String"
},
"traits": {
"smithy.api#uniqueItems": true
"smithy.api#uniqueItems": {}
}
}
}

View File

@ -79,7 +79,7 @@ The following example defines an operation that uses a custom endpoint:
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}.data."
}
@ -91,8 +91,8 @@ The following example defines an operation that uses a custom endpoint:
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true
"smithy.api#required": {},
"smithy.api#hostLabel": {}
}
}
}
@ -145,7 +145,7 @@ Given the following operation,
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}.data."
}
@ -157,8 +157,8 @@ Given the following operation,
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true
"smithy.api#required": {},
"smithy.api#hostLabel": {}
}
}
}
@ -214,7 +214,7 @@ Given the following operation,
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}-{bar}.data."
}
@ -226,15 +226,15 @@ Given the following operation,
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true
"smithy.api#required": {},
"smithy.api#hostLabel": {}
}
},
"bar": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true
"smithy.api#required": {},
"smithy.api#hostLabel": {}
}
}
}
@ -279,7 +279,7 @@ invalid because the ``{foo}`` and ``{bar}`` labels are adjacent:
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}{bar}.data."
}
@ -347,7 +347,7 @@ Given the following operation,
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}.data."
},
@ -363,8 +363,8 @@ Given the following operation,
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true,
"smithy.api#required": {},
"smithy.api#hostLabel": {},
"smithy.api#httpHeader": "X-Foo"
}
}
@ -438,7 +438,7 @@ to an operation marked with the :ref:`endpoint-trait` will be ignored.
"target": "smithy.example#GetStatusOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#endpoint": {
"hostPrefix": "{foo}.data."
}
@ -450,8 +450,8 @@ to an operation marked with the :ref:`endpoint-trait` will be ignored.
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#hostLabel": true
"smithy.api#required": {},
"smithy.api#hostLabel": {}
}
}
}

View File

@ -986,7 +986,7 @@ and HTTP bindings:
"messages": {
"target": "smithy.example#MessageStream",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}

View File

@ -59,13 +59,13 @@ syntax, similar to:
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"baz": {
"target": "smithy.api#Integer",
"traits": {
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
}
}

View File

@ -337,7 +337,7 @@ member:
"stringMember": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"numberMember": {

View File

@ -83,8 +83,8 @@ The following example defines a service that supports both the hypothetical
"type": "service",
"version": "2017-02-11",
"traits": {
"smithy.example#jsonExample": true,
"smithy.example#xmlExample": true
"smithy.example#jsonExample": {},
"smithy.example#xmlExample": {}
}
},
"smithy.example#jsonExample": {

View File

@ -63,13 +63,13 @@ to call ``CreateTable`` on a table that already exists will return an error.
"target": "smithy.example#CreateTable"
},
"traits": {
"smithy.api#noReplace": true
"smithy.api#noReplace": {}
}
},
"smithy.example#CreateTable": {
"type": "operation",
"traits": {
"smithy.api#idempotent": true
"smithy.api#idempotent": {}
}
}
}

View File

@ -529,7 +529,7 @@ Traits can be applied to the set shape and its members:
"target": "smithy.api#String"
},
"traits": {
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
}
}
@ -613,7 +613,7 @@ Traits can be applied to the map shape and its members:
"value": {
"target": "smithy.api#String",
"traits": {
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
},
"traits": {
@ -703,14 +703,14 @@ using the ``apply`` statement:
"target": "smithy.api#String",
"traits": {
"smithy.api#documentation": "This is documentation for `foo`.",
"smithy.api#required": true
"smithy.api#required": {}
}
},
"baz": {
"target": "smithy.api#Integer",
"traits": {
"smithy.api#documentation": "This is documentation for `baz`.",
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
}
},
@ -764,7 +764,7 @@ The following example defines a union shape with several members:
"stringB": {
"target": "smithy.api#String",
"traits": {
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
}
}
@ -1630,7 +1630,7 @@ For example, given the following model,
"target": "smithy.example#GetForecastOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"smithy.example#GetForecastInput": {
@ -1639,7 +1639,7 @@ For example, given the following model,
"forecastId": {
"target": "smithy.example#ForecastId",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -1650,7 +1650,7 @@ For example, given the following model,
"weather": {
"target": "smithy.example#WeatherData",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -1723,7 +1723,7 @@ Given the following model,
"forecasts": {
"target": "smithy.example#BatchPutForecastList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -352,14 +352,14 @@ service, followed by the events sent in the payload of the HTTP message.
"room": {
"target": "smithy.api#String",
"traits": {
"smithy.api#httpLabel:": true,
"smithy.api#required": true
"smithy.api#httpLabel:": {},
"smithy.api#required": {}
}
},
"messages": {
"target": "smithy.example#MessageStream",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -455,8 +455,8 @@ message.
"room": {
"target": "smithy.api#String",
"traits": {
"smithy.api#httpLabel:": true,
"smithy.api#required": true
"smithy.api#httpLabel:": {},
"smithy.api#required": {}
}
}
}
@ -473,7 +473,7 @@ message.
"messages": {
"target": "smithy.example#MessageStream",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -584,19 +584,19 @@ headers and the "c" member as the payload.
"a": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventPayload": true
"smithy.api#eventPayload": {}
}
},
"b": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventPayload": true
"smithy.api#eventPayload": {}
}
},
"c": {
"target": "smithy.api#Blob",
"traits": {
"smithy.api#eventPayload": true
"smithy.api#eventPayload": {}
}
}
}
@ -693,13 +693,13 @@ The following example defines multiple event headers:
"a": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventHeader": true
"smithy.api#eventHeader": {}
}
},
"b": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventHeader": true
"smithy.api#eventHeader": {}
}
}
}
@ -763,13 +763,13 @@ of an event:
"a": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventPayload": true
"smithy.api#eventPayload": {}
}
},
"b": {
"target": "smithy.api#String",
"traits": {
"smithy.api#eventHeader": true
"smithy.api#eventHeader": {}
}
}
}

View File

@ -43,7 +43,7 @@ to ``MyString``:
"type": "string",
"traits": {
"smithy.api#documentation": "Contains a string",
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
}
}
@ -189,8 +189,13 @@ traits if the shapes have no ``length`` constraints or ``required`` members.
Annotation traits
=================
A structure trait with no members is called an *annotation trait*. The
following example defines an annotation trait named ``foo``:
A structure trait with no members is called an *annotation trait*. It's hard
to predict what information a trait needs to capture when modeling a domain;
a trait might start out as a simple annotation, but later might benefit
from additional information. By defining an annotation trait rather than a
boolean trait, the trait can safely add optional members over time as needed.
The following example defines an annotation trait named ``foo``:
.. tabs::
@ -209,21 +214,13 @@ following example defines an annotation trait named ``foo``:
"smithy.example#foo": {
"type": "structure",
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
}
}
}
It's hard to predict what information a trait needs to capture when modeling
a domain; a trait might start out as a simple annotation, but later might need
additional information. By defining an annotation trait rather than a boolean
shape, the trait can safely add optional members over time as needed.
Smithy explicitly supports this use case by allowing ``true`` to be provided
for structure traits that have no required members.
The following applications of the ``foo`` annotation trait are all equivalent:
The following applications of the ``foo`` annotation trait are equivalent:
.. tabs::
@ -237,9 +234,6 @@ The following applications of the ``foo`` annotation trait are all equivalent:
@foo()
string MyString2
@foo(true)
string MyString3
.. code-tab:: json
{
@ -256,20 +250,14 @@ The following applications of the ``foo`` annotation trait are all equivalent:
"traits": {
"smithy.api#foo": {}
}
},
"smithy.example#MyString3": {
"type": "string",
"traits": {
"smithy.api#foo": true
}
}
}
}
A member can be safely added to an annotation trait if the member is not
marked as required. The applications of the ``foo`` trait in the previous
example and the following example are all valid even after adding a member
to the ``foo`` trait:
marked as :ref:`required <required-trait>`. The applications of the ``foo``
trait in the previous example and the following example are all valid even
after adding a member to the ``foo`` trait:
.. tabs::
@ -298,7 +286,7 @@ to the ``foo`` trait:
}
},
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
},
"smithy.example#MyString4": {
@ -404,7 +392,7 @@ the current namespace:
"smithy.example#MyString": {
"type": "string",
"traits": {
"smithy.api#myTraitName": true
"smithy.api#myTraitName": {}
}
}
}

View File

@ -56,7 +56,7 @@ is wrapped in an `Option type`_.
"smithy.example#BoxedInteger": {
"type": "integer",
"traits": {
"smithy.api#box": true
"smithy.api#box": {}
}
}
}

View File

@ -475,7 +475,7 @@ into a containing structure/union. For example, given the following:
"flatMap": {
"target": "smithy.example#MyMap",
"traits": {
"smithy.api#xmlFlattened": true
"smithy.api#xmlFlattened": {}
}
}
}
@ -635,7 +635,7 @@ member name. For example, given the following:
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#xmlAttribute": true
"smithy.api#xmlAttribute": {}
}
},
"bar": {
@ -678,7 +678,7 @@ Given the following:
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#xmlAttribute": true,
"smithy.api#xmlAttribute": {},
"smithy.api#xmlName": "NotFoo"
}
}
@ -737,7 +737,7 @@ Given the following:
"flat": {
"target": "smithy.example#MyList",
"traits": {
"smithy.api#xmlFlattened": true
"smithy.api#xmlFlattened": {}
}
},
"nested": {
@ -798,7 +798,7 @@ Maps can be flattened into structures too. Given the following:
"flat": {
"target": "smithy.example#MyMap",
"traits": {
"smithy.api#xmlFlattened": true
"smithy.api#xmlFlattened": {}
}
},
"notFlat": {

View File

@ -131,15 +131,15 @@ and ``{second}``, in the MQTT topic template:
"first": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.mqtt#topicLabel": true
"smithy.api#required": {},
"smithy.mqtt#topicLabel": {}
}
},
"second": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.mqtt#topicLabel": true
"smithy.api#required": {},
"smithy.mqtt#topicLabel": {}
}
},
"message": {
@ -240,8 +240,8 @@ The following example defines an operation that publishes messages to the
"bar": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.mqtt#topicLabel": true
"smithy.api#required": {},
"smithy.mqtt#topicLabel": {}
}
},
"message": {
@ -365,8 +365,8 @@ topic using an :ref:`event stream <event-streams>`:
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.mqtt#topicLabel": true
"smithy.api#required": {},
"smithy.mqtt#topicLabel": {}
}
}
}

View File

@ -20,7 +20,7 @@
"target": "example.smithy#GetPayloadOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#http": {
"uri": "/test",
"method": "GET"

View File

@ -37,7 +37,7 @@
"target": "example.smithy#OtherOperationOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#http": {
"uri": "/bar",
"method": "GET"
@ -50,8 +50,8 @@
"payload": {
"target": "example.smithy#InboundBinaryPayload",
"traits": {
"smithy.api#required": true,
"smithy.api#httpPayload": true
"smithy.api#required": {},
"smithy.api#httpPayload": {}
}
}
}
@ -62,8 +62,8 @@
"payload": {
"target": "example.smithy#OutboundBinaryPayload",
"traits": {
"smithy.api#required": true,
"smithy.api#httpPayload": true
"smithy.api#required": {},
"smithy.api#httpPayload": {}
}
}
}

View File

@ -59,7 +59,7 @@
"target": "example.smithy#PutPayloadInput"
},
"traits": {
"smithy.api#idempotent": true,
"smithy.api#idempotent": {},
"smithy.api#http": {
"uri": "/payload/{id}",
"method": "PUT",
@ -76,7 +76,7 @@
"target": "example.smithy#GetPayloadOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#http": {
"uri": "/payload/{id}",
"method": "GET"
@ -89,7 +89,7 @@
"target": "example.smithy#DeletePayloadInput"
},
"traits": {
"smithy.api#idempotent": true,
"smithy.api#idempotent": {},
"smithy.api#http": {
"uri": "/payload/{id}",
"method": "DELETE",
@ -103,7 +103,7 @@
"target": "example.smithy#ListPayloadsOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#http": {
"uri": "/payload",
"method": "GET"
@ -116,8 +116,8 @@
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#httpLabel": true
"smithy.api#required": {},
"smithy.api#httpLabel": {}
}
},
"header": {
@ -141,7 +141,7 @@
"body": {
"target": "smithy.api#Blob",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -152,8 +152,8 @@
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#httpLabel": true,
"smithy.api#required": true
"smithy.api#httpLabel": {},
"smithy.api#required": {}
}
}
}
@ -170,7 +170,7 @@
"body": {
"target": "smithy.api#Blob",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -181,8 +181,8 @@
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#httpLabel": true,
"smithy.api#required": true
"smithy.api#httpLabel": {},
"smithy.api#required": {}
}
}
}
@ -222,13 +222,13 @@
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"createdAt": {
"target": "smithy.api#Timestamp",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -20,7 +20,7 @@
"smithy.example#Operation1": {
"type": "operation",
"traits": {
"smithy.api#idempotent": true,
"smithy.api#idempotent": {},
"smithy.api#http": {
"uri": "/1",
"method": "PUT"

View File

@ -40,7 +40,7 @@
"scheme": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#required": {},
"smithy.api#documentation": "The Smithy authentication scheme used by the client (e.g, aws.v4).",
"smithy.api#idRef": {
"selector": "[trait|authDefinition]",
@ -94,7 +94,7 @@
},
"traits": {
"smithy.api#documentation": "An object that associates an authorizer and associated metadata with an authentication mechanism.",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#authorizer": {
@ -127,13 +127,13 @@
"type": {
"target": "aws.apigateway#IntegrationType",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"uri": {
"target": "aws.apigateway#Arn",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"credentials": {
@ -142,7 +142,7 @@
"httpMethod": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"passThroughBehavior": {
@ -248,7 +248,7 @@
"aws.api#arnReference": {
"type": "AWS::IAM::Role"
},
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#Arn": {
@ -256,7 +256,7 @@
"traits": {
"smithy.api#documentation": "<p>The ARN of an AWS integration target.</p><p>This string MAY contain the literal string <code>{serviceName}</code> and/or the literal string <code>{operationName}</code>, which will be replaced with the name of the Smithy service shape and the name of the Smithy operation shape, respectively.</p>",
"aws.api#arnReference": {},
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#RequestParameters": {
@ -269,7 +269,7 @@
},
"traits": {
"smithy.api#documentation": "<p>A mapping of integration request parameters to the API Gateway data mapping expression that should be used to populate the parameter.</p> <p><strong>Note:</strong> This feature is provided primarily to allow injecting static values and context variables for request parameters. Request data MAY be mapped to headers using the syntax described in <a href=\"https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html#mapping-response-parameters\">the API Gateway Developer Guide</a>; however, the data must be identified according to its location in the serialized request, which may differ from protocol to protocol. Here be dragons!</p>",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#Templates": {
@ -282,13 +282,13 @@
},
"traits": {
"smithy.api#documentation": "<p>A map of MIME types to velocity templates allowing you to craft a new integration message from received data.</p><p><strong>Note:</strong> This feature is provided primarily to allow injecting static values and context variables for request parameters. Request data MAY be mapped to headers using the syntax described in <a href=\"https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html#mapping-response-parameters\">the API Gateway Developer Guide</a>; however, the data must be identified according to its location in the serialized request, which may differ from protocol to protocol. Here be dragons!</p>",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#ConnectionType": {
"type": "string",
"traits": {
"smithy.api#private": true,
"smithy.api#private": {},
"smithy.api#enum": [
{"value": "INTERNET"},
{"value": "VPC_LINK"}
@ -313,7 +313,7 @@
"name": "NEVER"
}
],
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#ContentHandling": {
@ -324,7 +324,7 @@
{"value": "CONVERT_TO_TEXT", "name": "CONVERT_TO_TEXT"},
{"value": "CONVERT_TO_BINARY", "name": "CONVERT_TO_BINARY"}
],
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#StringList": {
@ -343,7 +343,7 @@
},
"traits": {
"smithy.api#documentation": "A map of response identifiers to responses.",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#IntegrationResponse": {
@ -364,7 +364,7 @@
},
"traits": {
"smithy.api#documentation": "Defines a response and specifies parameter mappings.",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.apigateway#ResponseParameters": {
@ -377,7 +377,7 @@
},
"traits": {
"smithy.api#documentation": "Specifies parameter mappings for the response. Only the header and body parameters of the integration response can be mapped to the header parameters of the method.",
"smithy.api#private": true
"smithy.api#private": {}
}
}
}

View File

@ -17,12 +17,12 @@ package software.amazon.smithy.aws.iam.traits;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
/**
* Disables the automatic inference of condition keys of a resource.
*/
public final class DisableConditionKeyInferenceTrait extends BooleanTrait {
public final class DisableConditionKeyInferenceTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.iam#disableConditionKeyInference");
public DisableConditionKeyInferenceTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class DisableConditionKeyInferenceTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<DisableConditionKeyInferenceTrait> {
public static final class Provider extends AnnotationTrait.Provider<DisableConditionKeyInferenceTrait> {
public Provider() {
super(ID, DisableConditionKeyInferenceTrait::new);
}

View File

@ -61,7 +61,7 @@
"aws.iam#IamIdentifier": {
"type": "string",
"traits": {
"smithy.api#private": true,
"smithy.api#private": {},
"smithy.api#pattern": "^([A-Za-z0-9][A-Za-z0-9-\\.]{0,62}:[^:]+)$"
}
},
@ -77,18 +77,18 @@
"type": {
"target": "aws.iam#ConditionKeyType",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
},
"traits": {
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.iam#ConditionKeyType": {
"type": "string",
"traits": {
"smithy.api#private": true,
"smithy.api#private": {},
"smithy.api#documentation": "The IAM policy type of the value that will supplied for this context key",
"smithy.api#enum": [
{"value": "ARN"},

View File

@ -17,9 +17,9 @@ package software.amazon.smithy.aws.traits;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
public final class ControlPlaneTrait extends BooleanTrait {
public final class ControlPlaneTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.api#controlPlane");
public ControlPlaneTrait(SourceLocation sourceLocation) {
@ -30,7 +30,7 @@ public final class ControlPlaneTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<ControlPlaneTrait> {
public static final class Provider extends AnnotationTrait.Provider<ControlPlaneTrait> {
public Provider() {
super(ID, ControlPlaneTrait::new);
}

View File

@ -17,9 +17,9 @@ package software.amazon.smithy.aws.traits;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
public final class DataPlaneTrait extends BooleanTrait {
public final class DataPlaneTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.api#dataPlane");
public DataPlaneTrait(SourceLocation sourceLocation) {
@ -30,7 +30,7 @@ public final class DataPlaneTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<DataPlaneTrait> {
public static final class Provider extends AnnotationTrait.Provider<DataPlaneTrait> {
public Provider() {
super(ID, DataPlaneTrait::new);
}

View File

@ -18,12 +18,12 @@ package software.amazon.smithy.aws.traits.auth;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
/**
* Indicates that the payload of an operation is not to be signed.
*/
public final class UnsignedPayloadTrait extends BooleanTrait {
public final class UnsignedPayloadTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.auth#unsignedPayload");
public UnsignedPayloadTrait(FromSourceLocation sourceLocation) {
@ -34,7 +34,7 @@ public final class UnsignedPayloadTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<UnsignedPayloadTrait> {
public static final class Provider extends AnnotationTrait.Provider<UnsignedPayloadTrait> {
public Provider() {
super(ID, UnsignedPayloadTrait::new);
}

View File

@ -17,13 +17,13 @@ package software.amazon.smithy.aws.traits.clientendpointdiscovery;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
/**
* Indicates members of the operation input which should be use to discover
* endpoints.
*/
public final class ClientEndpointDiscoveryIdTrait extends BooleanTrait {
public final class ClientEndpointDiscoveryIdTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.api#clientEndpointDiscoveryId");
public ClientEndpointDiscoveryIdTrait(SourceLocation sourceLocation) {
@ -34,7 +34,7 @@ public final class ClientEndpointDiscoveryIdTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<ClientEndpointDiscoveryIdTrait> {
public static final class Provider extends AnnotationTrait.Provider<ClientEndpointDiscoveryIdTrait> {
public Provider() {
super(ID, ClientEndpointDiscoveryIdTrait::new);
}

View File

@ -17,7 +17,7 @@ package software.amazon.smithy.aws.traits.protocols;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
/**
* An RPC-based protocol that sends query string requests and XML responses.
@ -25,7 +25,7 @@ import software.amazon.smithy.model.traits.BooleanTrait;
* <p>This protocol is deprecated. For new services, ise {@link RestJson1Trait}
* or {@link AwsJson1_1Trait} instead.
*/
public final class AwsQueryTrait extends BooleanTrait {
public final class AwsQueryTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.protocols#awsQuery");
@ -37,7 +37,7 @@ public final class AwsQueryTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<AwsQueryTrait> {
public static final class Provider extends AnnotationTrait.Provider<AwsQueryTrait> {
public Provider() {
super(ID, AwsQueryTrait::new);
}

View File

@ -17,7 +17,7 @@ package software.amazon.smithy.aws.traits.protocols;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
/**
* An RPC-based protocol that sends query string requests and XML responses,
@ -26,7 +26,7 @@ import software.amazon.smithy.model.traits.BooleanTrait;
* <p>This protocol is deprecated. For new services, ise {@link RestJson1Trait}
* or {@link AwsJson1_1Trait} instead.
*/
public final class Ec2QueryTrait extends BooleanTrait {
public final class Ec2QueryTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("aws.protocols#ec2Query");
@ -38,7 +38,7 @@ public final class Ec2QueryTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<Ec2QueryTrait> {
public static final class Provider extends AnnotationTrait.Provider<Ec2QueryTrait> {
public Provider() {
super(ID, Ec2QueryTrait::new);
}

View File

@ -7,7 +7,7 @@
"template": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"absolute": {
@ -36,7 +36,7 @@
"sdkId": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"arnNamespace": {
@ -139,14 +139,14 @@
"type": "string",
"traits": {
"smithy.api#pattern": "^[a-z0-9.\\-]{1,63}$",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.api#CloudFormationName": {
"type": "string",
"traits": {
"smithy.api#pattern": "^[A-Z][A-Za-z0-9]+$",
"smithy.api#private": true
"smithy.api#private": {}
}
},
"aws.api#clientDiscoveredEndpoint": {
@ -155,7 +155,7 @@
"required": {
"target": "smithy.api#Boolean",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
},
@ -167,7 +167,7 @@
}
},
"aws.api#clientEndpointDiscoveryId": {
"type": "boolean",
"type": "structure",
"traits": {
"smithy.api#trait": {
"selector": "operation[trait|aws.api#clientDiscoveredEndpoint] -[input]-> structure > :test(member[trait|required] > string)"
@ -185,7 +185,7 @@
"failWhenMissing": true,
"selector": "operation"
},
"smithy.api#required": true,
"smithy.api#required": {},
"smithy.api#documentation": "Indicates the operation that clients should use to discover endpoints for the service."
}
},
@ -196,7 +196,7 @@
"failWhenMissing": true,
"selector": "structure[trait|error]"
},
"smithy.api#required": true,
"smithy.api#required": {},
"smithy.api#documentation": "Indicates the error that tells clients that the endpoint they are using is no longer valid. This error MUST be bound to any operation bound to the service which is marked with the aws.api#clientDiscoveredEndpoint trait."
}
}

View File

@ -7,7 +7,7 @@
"name": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true,
"smithy.api#required": {},
"smithy.api#documentation": "The signature version 4 service signing name to use in the credential scope when signing requests. This value SHOULD match the `arnNamespace` property of the `aws.api#service-trait`.",
"smithy.api#externalDocumentation": {
"Reference": "https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html"
@ -45,7 +45,7 @@
"providerArns": {
"target": "aws.auth#StringList",
"traits": {
"smithy.api#required": true,
"smithy.api#required": {},
"smithy.api#documentation": "A list of the Amazon Cognito user pool ARNs. Each element is of this format: arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}."
}
}
@ -54,7 +54,7 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#authDefinition": true,
"smithy.api#authDefinition": {},
"smithy.api#documentation": "Configures an Amazon Cognito User Pools auth scheme.",
"smithy.api#tags": [
"internal"
@ -67,7 +67,7 @@
"target": "smithy.api#String"
},
"traits": {
"smithy.api#private": true
"smithy.api#private": {}
}
}
}

View File

@ -63,7 +63,7 @@
]
},
"smithy.api#documentation": "A RESTful protocol that sends XML in structured payloads.",
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
},
"aws.protocols#awsJson1_0": {
@ -125,7 +125,7 @@
]
},
"smithy.api#documentation": "An RPC-based protocol that sends query string requests and XML responses. This protocol does not use HTTP binding traits.",
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
},
"aws.protocols#ec2Query": {
@ -144,7 +144,7 @@
]
},
"smithy.api#documentation": "An RPC-based protocol that sends Amazon EC2 formatted query string requests and XML responses. This protocol does not use HTTP binding traits.",
"smithy.api#deprecated": true
"smithy.api#deprecated": {}
}
},
"aws.protocols#ec2QueryName": {
@ -163,7 +163,7 @@
"target": "smithy.api#String"
},
"traits": {
"smithy.api#private": true
"smithy.api#private": {}
}
}
}

View File

@ -4,7 +4,7 @@
"ns.foo#Unsigned1": {
"type": "operation",
"traits": {
"aws.auth#unsignedPayload": true
"aws.auth#unsignedPayload": {}
}
}
}

View File

@ -98,8 +98,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -150,8 +150,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -93,8 +93,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -95,8 +95,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#endpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#endpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -110,8 +110,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}
@ -146,8 +146,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
},
"Object": {

View File

@ -63,7 +63,7 @@
"id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -96,8 +96,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -90,8 +90,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -101,8 +101,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -147,8 +147,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -98,8 +98,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -93,7 +93,7 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -128,7 +128,7 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
},
"Object": {

View File

@ -70,8 +70,8 @@
"Id": {
"target": "smithy.api#String",
"traits": {
"aws.api#clientEndpointDiscoveryId": true,
"smithy.api#required": true
"aws.api#clientEndpointDiscoveryId": {},
"smithy.api#required": {}
}
}
}

View File

@ -21,19 +21,19 @@
}
],
"traits": {
"aws.api#controlPlane": true
"aws.api#controlPlane": {}
}
},
"smithy.example#Operation1": {
"type": "operation",
"traits": {
"aws.api#dataPlane": true
"aws.api#dataPlane": {}
}
},
"smithy.example#Operation2": {
"type": "operation",
"traits": {
"aws.api#controlPlane": true
"aws.api#controlPlane": {}
}
},
"smithy.example#Operation3": {
@ -61,13 +61,13 @@
"smithy.example#Operation4": {
"type": "operation",
"traits": {
"aws.api#dataPlane": true
"aws.api#dataPlane": {}
}
},
"smithy.example#Operation5": {
"type": "operation",
"traits": {
"aws.api#controlPlane": true
"aws.api#controlPlane": {}
}
},
"smithy.example#Operation6": {
@ -84,13 +84,13 @@
}
],
"traits": {
"aws.api#dataPlane": true
"aws.api#dataPlane": {}
}
},
"smithy.example#Operation7": {
"type": "operation",
"traits": {
"aws.api#controlPlane": true
"aws.api#controlPlane": {}
}
},
"smithy.example#Operation8": {

View File

@ -4,7 +4,7 @@
"com.foo#String": {
"type": "apply",
"traits": {
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
}
}

View File

@ -7,7 +7,7 @@
"smithy.example#Ctrait": {
"type": "structure",
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
}
},

View File

@ -30,7 +30,7 @@
"target": "ns.foo#GetMyResourceInput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#GetMyResourceInput": {
@ -39,8 +39,8 @@
"id": {
"target": "ns.foo#MyResourceId",
"traits": {
"smithy.api#required": true,
"smithy.api#sensitive": true
"smithy.api#required": {},
"smithy.api#sensitive": {}
}
}
}

View File

@ -9,7 +9,7 @@
}
},
"traits": {
"smithy.api#trait": true,
"smithy.api#trait": {},
"smithy.api#tags": [
"foo",
"baz"
@ -24,7 +24,7 @@
}
},
"traits": {
"smithy.api#trait": true,
"smithy.api#trait": {},
"smithy.api#tags": [
"foo",
"qux"

View File

@ -22,7 +22,7 @@
"target": "example.rest#PutPayloadOutput"
},
"traits": {
"smithy.api#idempotent": true,
"smithy.api#idempotent": {},
"smithy.api#deprecated": {},
"smithy.api#http": {
"uri": "/payload/{path}",
@ -36,8 +36,8 @@
"path": {
"target": "smithy.api#String",
"traits": {
"smithy.api#httpLabel": true,
"smithy.api#required": true
"smithy.api#httpLabel": {},
"smithy.api#required": {}
}
},
"header": {
@ -67,7 +67,7 @@
"body": {
"target": "example.rest#Blob",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -84,7 +84,7 @@
"body": {
"target": "example.rest#Blob",
"traits": {
"smithy.api#httpPayload": true
"smithy.api#httpPayload": {}
}
}
}
@ -116,7 +116,7 @@
"abc": {
"target": "smithy.api#String",
"traits": {
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
},
"def": {
@ -173,7 +173,7 @@
"value": {
"target": "smithy.api#String",
"traits": {
"smithy.api#sensitive": true
"smithy.api#sensitive": {}
}
}
},

View File

@ -4,13 +4,13 @@
"ns.foo#InvalidTrait": {
"type": "string",
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
},
"ns.foo#validTrait": {
"type": "string",
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
},
"ns.foo#lowerStructureTrait": {
@ -21,7 +21,7 @@
}
},
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
},
"ns.foo#upperStructureTrait": {
@ -35,7 +35,7 @@
}
},
"traits": {
"smithy.api#trait": true
"smithy.api#trait": {}
}
},
"ns.foo#Foo": {
@ -93,7 +93,7 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#authDefinition": true
"smithy.api#authDefinition": {}
}
}
},

View File

@ -104,7 +104,7 @@
}
],
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#OperationAInput": {
@ -153,7 +153,7 @@
"id": {
"target": "ns.foo#MyResourceIdentifier",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -84,7 +84,7 @@
}
],
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#OperationAInput": {
@ -130,7 +130,7 @@
"id": {
"target": "ns.foo#MyResourceIdentifier",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}

View File

@ -34,7 +34,7 @@
"target": "ns.foo#AOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#AInput": {
@ -52,7 +52,7 @@
"target": "ns.foo#AOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#C": {
@ -64,7 +64,7 @@
"target": "ns.foo#CInputOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#CInputOutput": {
@ -79,7 +79,7 @@
"target": "ns.foo#DInputOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#DInputOutput": {
@ -91,7 +91,7 @@
"target": "ns.foo#DInputOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#F": {
@ -100,7 +100,7 @@
"target": "ns.foo#DInputOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
}
},

View File

@ -16,7 +16,7 @@
"ns.foo#ListFoos": {
"type": "operation",
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#IgnoreMe": {
@ -25,7 +25,7 @@
"target": "ns.foo#IgnoreMeOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#IgnoreMeOutput": {
@ -40,7 +40,7 @@
"target": "ns.foo#IgnoreMeTooOutput"
},
"traits": {
"smithy.api#readonly": true,
"smithy.api#readonly": {},
"smithy.api#paginated": {
"inputToken": "nextToken",
"outputToken": "nextToken",
@ -69,7 +69,7 @@
"items": {
"target": "ns.foo#StringList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -80,7 +80,7 @@
"target": "ns.foo#AInput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#AInput": {
@ -97,7 +97,7 @@
"target": "ns.foo#BOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#BOutput": {
@ -114,7 +114,7 @@
"target": "ns.foo#GetFoosOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#GetFoosOutput": {
@ -123,7 +123,7 @@
"items": {
"target": "ns.foo#StringList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -134,7 +134,7 @@
"target": "ns.foo#GetBarsOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#GetBarsOutput": {
@ -144,7 +144,7 @@
"ns.foo#SchwiftyFoos": {
"type": "operation",
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#SquanchFoos": {
@ -153,7 +153,7 @@
"target": "ns.foo#SquanchFoosOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#SquanchFoosOutput": {
@ -162,7 +162,7 @@
"items": {
"target": "ns.foo#StringList",
"traits": {
"smithy.api#required": true
"smithy.api#required": {}
}
}
}
@ -173,7 +173,7 @@
"target": "ns.foo#SomeOperationInput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#SomeOperationInput": {
@ -190,7 +190,7 @@
"target": "ns.foo#SomeOtherOperationOutput"
},
"traits": {
"smithy.api#readonly": true
"smithy.api#readonly": {}
}
},
"ns.foo#SomeOtherOperationOutput": {

View File

@ -196,6 +196,9 @@ enum AstModelLoader {
private void applyTraits(ShapeId id, ObjectNode traits, LoaderVisitor visitor) {
for (Map.Entry<StringNode, Node> traitNode : traits.getMembers().entrySet()) {
ShapeId traitId = traitNode.getKey().expectShapeId();
// JSON AST model traits are never considered annotation traits, meaning
// that a null value provided in the AST is not coerced in the same way
// as an omitted value in the IDL (e.g., "@foo").
visitor.onTrait(id, traitId, traitNode.getValue());
}
}

View File

@ -141,7 +141,7 @@ final class IdlModelLoader {
private final String filename;
private final SmithyModelLexer lexer;
private final LoaderVisitor visitor;
private final List<Pair<String, Node>> pendingTraits = new ArrayList<>();
private final List<TraitEntry> pendingTraits = new ArrayList<>();
/** Map of shape aliases to their targets. */
private final Map<String, ShapeId> useShapes = new HashMap<>();
@ -187,6 +187,19 @@ final class IdlModelLoader {
}
}
// A pending trait that also doesn't yet have a resolved trait shape ID.
private static final class TraitEntry {
final String traitName;
final Node value;
final boolean isAnnotation;
TraitEntry(String traitName, Node value, boolean isAnnotation) {
this.traitName = traitName;
this.value = value;
this.isAnnotation = isAnnotation;
}
}
public static void load(String path, Supplier<InputStream> contentSupplier, LoaderVisitor visitor) {
try (SmithyModelLexer lexer = new SmithyModelLexer(path, contentSupplier.get())) {
new IdlModelLoader(path, lexer, visitor);
@ -389,13 +402,13 @@ final class IdlModelLoader {
}
}
private Pair<String, Node> parseTraitValue(Token token, TraitValueType type) {
private TraitEntry parseTraitValue(Token token, TraitValueType type) {
try {
requireNamespaceOrThrow();
// Resolve the trait name and ensure that the trait forms a syntactically valid value.
ShapeId.fromOptionalNamespace(namespace, token.lexeme);
Pair<String, Node> result = Pair.of(token.lexeme, parseTraitValueBody());
TraitEntry result = parseTraitValueBody(token.lexeme);
// `apply` doesn't require any specific token to follow.
if (type == TraitValueType.APPLY) {
@ -427,10 +440,10 @@ final class IdlModelLoader {
}
}
private Node parseTraitValueBody() {
private TraitEntry parseTraitValueBody(String traitName) {
// Null is coerced into the appropriate type for the trait.
if (!test(LPAREN)) {
return new NullNode(currentLocation());
return new TraitEntry(traitName, new NullNode(currentLocation()), true);
}
expect(LPAREN);
@ -438,14 +451,14 @@ final class IdlModelLoader {
if (next.type == RPAREN) {
// An open and closed "()" signals an empty object.
return new ObjectNode(MapUtils.of(), next.getSourceLocation());
return new TraitEntry(traitName, new ObjectNode(MapUtils.of(), next.getSourceLocation()), false);
}
// Test to see if this is just a string or if it's an object.
if (test(COLON)) {
if (next.type == QUOTED || next.type == UNQUOTED) {
// Parse the object using the already parsed key.
return parseObjectNodeWithKey(currentLocation(), RPAREN, next);
return new TraitEntry(traitName, parseObjectNodeWithKey(currentLocation(), RPAREN, next), false);
}
throw syntax("Expected a string to start a trait value object");
}
@ -460,7 +473,7 @@ final class IdlModelLoader {
}
expect(RPAREN);
return result;
return new TraitEntry(traitName, result, false);
}
private Node parseNode() {
@ -616,8 +629,8 @@ final class IdlModelLoader {
try {
ShapeId id = ShapeId.fromRelative(namespace, name);
for (Pair<String, Node> pair : pendingTraits) {
onDeferredTrait(id, pair.getLeft(), pair.getRight());
for (TraitEntry traitEntry : pendingTraits) {
onDeferredTrait(id, traitEntry.traitName, traitEntry.value, traitEntry.isAnnotation);
}
pendingTraits.clear();
collectPendingDocString(id);
@ -637,9 +650,16 @@ final class IdlModelLoader {
* @param target Shape to add the trait to.
* @param traitName Trait name to add.
* @param traitValue Trait value as a Node object.
* @param isAnnotation Set to true to indicate that the value for the trait was omitted.
*/
private void onDeferredTrait(ShapeId target, String traitName, Node traitValue) {
onShapeTarget(traitName, traitValue.getSourceLocation(), id -> visitor.onTrait(target, id, traitValue));
private void onDeferredTrait(ShapeId target, String traitName, Node traitValue, boolean isAnnotation) {
onShapeTarget(traitName, traitValue.getSourceLocation(), id -> {
if (isAnnotation) {
visitor.onAnnotationTrait(target, id, traitValue.expectNullNode());
} else {
visitor.onTrait(target, id, traitValue);
}
});
}
private boolean isRealizedShapeId(ShapeId expectedId, String target) {
@ -670,7 +690,7 @@ final class IdlModelLoader {
private void parseStructuredContents(String shapeType, ShapeId parent, Collection<String> requiredMembers) {
expect(LBRACE);
List<Pair<String, Node>> memberTraits = new ArrayList<>();
List<TraitEntry> memberTraits = new ArrayList<>();
Set<String> remainingMembers = requiredMembers.isEmpty() ? SetUtils.of() : new HashSet<>(requiredMembers);
Token token = expect(ANNOTATION, QUOTED, UNQUOTED, RBRACE, DOC);
@ -697,8 +717,8 @@ final class IdlModelLoader {
expect(COLON);
parseMember(memberId);
// Add the loaded traits on the member now that the ID is known.
for (Pair<String, Node> pair : memberTraits) {
onDeferredTrait(memberId, pair.getLeft(), pair.getRight());
for (TraitEntry traitEntry : memberTraits) {
onDeferredTrait(memberId, traitEntry.traitName, traitEntry.value, traitEntry.isAnnotation);
}
memberTraits.clear();
collectPendingDocString(memberId);
@ -753,13 +773,13 @@ final class IdlModelLoader {
Token nextToken = expect(UNQUOTED);
String name = nextToken.lexeme;
Token token = expect(ANNOTATION);
Pair<String, Node> trait = parseTraitValue(token, TraitValueType.APPLY);
TraitEntry traitEntry = parseTraitValue(token, TraitValueType.APPLY);
expectNewline();
// First, resolve the targeted shape.
onShapeTarget(name, nextToken.getSourceLocation(), id -> {
// Next, resolve the trait ID.
onDeferredTrait(id, trait.getLeft(), trait.getRight());
onDeferredTrait(id, traitEntry.traitName, traitEntry.value, traitEntry.isAnnotation);
});
}

View File

@ -34,6 +34,7 @@ import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.ExpectationNotMetException;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NullNode;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.AbstractShapeBuilder;
import software.amazon.smithy.model.shapes.MemberShape;
@ -96,12 +97,14 @@ final class LoaderVisitor {
final ShapeId id;
final Node value;
final Trait trait;
final boolean isAnnotation;
// A pending trait that needs to be created.
PendingTrait(ShapeId id, Node value) {
PendingTrait(ShapeId id, Node value, boolean isAnnotation) {
this.id = id;
this.value = value;
this.trait = null;
this.isAnnotation = isAnnotation;
}
// A pending trait that's already created.
@ -109,6 +112,7 @@ final class LoaderVisitor {
this.id = id;
this.trait = trait;
this.value = null;
isAnnotation = false;
}
}
@ -266,10 +270,28 @@ final class LoaderVisitor {
* if the trait name is not absolute.
*
* @param target Shape to add the trait to.
* @param trait SHape ID of the trait to add.
* @param trait Shape ID of the trait to add.
* @param traitValue Trait value as a Node object.
*/
public void onTrait(ShapeId target, ShapeId trait, Node traitValue) {
onTrait(target, trait, traitValue, false);
}
/**
* Adds an annotation trait to a shape.
*
* <p>An annotation trait has no value, and it's value is coerced from
* null into an object or array.
*
* @param target Shape to add the trait to.
* @param trait Shape ID of the trait to add.
* @param traitValue Trait value as a Node object.
*/
public void onAnnotationTrait(ShapeId target, ShapeId trait, NullNode traitValue) {
onTrait(target, trait, traitValue, true);
}
private void onTrait(ShapeId target, ShapeId trait, Node traitValue, boolean isAnnotation) {
// Special handling for the loading of trait definitions. These need to be
// loaded first before other traits can be resolved.
if (trait.equals(TraitDefinition.ID)) {
@ -279,7 +301,7 @@ final class LoaderVisitor {
// Add the definition trait to the shape.
onTrait(target, traitDef);
} else {
PendingTrait pendingTrait = new PendingTrait(trait, traitValue);
PendingTrait pendingTrait = new PendingTrait(trait, traitValue, isAnnotation);
addPendingTrait(target, traitValue.getSourceLocation(), trait, pendingTrait);
}
}
@ -529,7 +551,7 @@ final class LoaderVisitor {
}
ShapeId traitId = trait.id;
Node value = coerceTraitValue(trait.value, trait.id);
Node value = coerceTraitValue(trait);
Node previous = traits.get(traitId);
if (previous == null) {
@ -548,8 +570,32 @@ final class LoaderVisitor {
return traits;
}
private Node coerceTraitValue(Node value, ShapeId traitId) {
return Trait.coerceTraitValue(value, determineTraitDefinitionType(traitId));
/**
* Coerces a null annotation trait value for the given type.
*
* <p>Null values provided for traits are coerced in some cases to fit
* the type referenced by the shape. This is used in the .smithy format
* to make is so that you can write "@foo" rather than "@foo([])".
*
* <ul>
* <li>Structure and map traits are converted to an empty object.</li>
* <li>List and set traits are converted to an empty array.</li>
* </ul>
*
* @param trait Trait to coerce.
* @return Returns the coerced value.
*/
private Node coerceTraitValue(PendingTrait trait) {
if (trait.isAnnotation && trait.value.isNullNode()) {
ShapeType targetType = determineTraitDefinitionType(trait.id);
if (targetType == ShapeType.STRUCTURE || targetType == ShapeType.MAP) {
return new ObjectNode(Collections.emptyMap(), trait.value.getSourceLocation());
} else if (targetType == ShapeType.LIST || targetType == ShapeType.SET) {
return new ArrayNode(Collections.emptyList(), trait.value.getSourceLocation());
}
}
return trait.value;
}
private ShapeType determineTraitDefinitionType(ShapeId traitId) {

View File

@ -39,7 +39,7 @@ import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NumberNode;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.StringNode;
import software.amazon.smithy.model.traits.BooleanTrait;
import software.amazon.smithy.model.traits.AnnotationTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.IdRefTrait;
import software.amazon.smithy.model.traits.Trait;
@ -371,9 +371,9 @@ public final class SmithyIdlModelSerializer {
Node node = trait.toNode();
Shape shape = model.expectShape(trait.toShapeId());
if (trait instanceof BooleanTrait || isEmptyStructure(node, shape)) {
// Traits that inherit from BooleanTrait specifically can omit a value.
// Traits that are simply boolean shapes which don't implement BooleanTrait cannot.
if (trait instanceof AnnotationTrait || isEmptyStructure(node, shape)) {
// Traits that inherit from AnnotationTrait specifically can omit a value.
// Traits that are simply boolean shapes which don't implement AnnotationTrait cannot.
// Additionally, empty structure traits can omit a value.
codeWriter.write("@$I", trait.toShapeId());
} else if (node.isObjectNode()) {

View File

@ -15,31 +15,31 @@
package software.amazon.smithy.model.traits;
import java.util.Collections;
import java.util.function.Function;
import software.amazon.smithy.model.SourceException;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.BooleanNode;
import software.amazon.smithy.model.node.ExpectationNotMetException;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
/**
* Trait implementation that expects an empty object or a boolean
* value of true.
* Trait implementation for traits that are an empty object.
*/
public abstract class BooleanTrait extends AbstractTrait {
public BooleanTrait(ShapeId id, SourceLocation sourceLocation) {
public abstract class AnnotationTrait extends AbstractTrait {
public AnnotationTrait(ShapeId id, SourceLocation sourceLocation) {
super(id, sourceLocation);
}
@Override
protected final Node createNode() {
return new BooleanNode(true, getSourceLocation());
return new ObjectNode(Collections.emptyMap(), getSourceLocation());
}
/**
* Trait provider that expects a boolean value of true.
*/
public static class Provider<T extends BooleanTrait> extends AbstractTrait.Provider {
public static class Provider<T extends AnnotationTrait> extends AbstractTrait.Provider {
private final Function<SourceLocation, T> traitFactory;
/**
@ -53,17 +53,13 @@ public abstract class BooleanTrait extends AbstractTrait {
@Override
public T createTrait(ShapeId id, Node value) {
if (value.asObjectNode().isPresent() && value.asObjectNode().get().getMembers().isEmpty()) {
if (value.isObjectNode()) {
return traitFactory.apply(value.getSourceLocation());
}
BooleanNode booleanNode = value.expectBooleanNode();
if (!booleanNode.getValue()) {
throw new SourceException(String.format(
"Boolean trait `%s` expects a value of `true`, but found `false`", getShapeId()), value);
}
return traitFactory.apply(value.getSourceLocation());
throw new ExpectationNotMetException(String.format(
"Annotation traits must be an object or omitted in the IDL, but found %s",
value.getType()), value);
}
}
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that a shape is boxed, meaning a value may or may not be present.
*/
public final class BoxTrait extends BooleanTrait {
public final class BoxTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#box");
public BoxTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class BoxTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<BoxTrait> {
public static final class Provider extends AnnotationTrait.Provider<BoxTrait> {
public Provider() {
super(ID, BoxTrait::new);
}

View File

@ -24,7 +24,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* <p>This trait can targets members of a structure marked with the event
* trait that targets blob, boolean, integer, long, string, or timestamp.
*/
public final class EventHeaderTrait extends BooleanTrait {
public final class EventHeaderTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#eventHeader");
public EventHeaderTrait(SourceLocation sourceLocation) {
@ -35,7 +35,7 @@ public final class EventHeaderTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<EventHeaderTrait> {
public static final class Provider extends AnnotationTrait.Provider<EventHeaderTrait> {
public Provider() {
super(ID, EventHeaderTrait::new);
}

View File

@ -24,7 +24,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* This trait can targets members of a structure marked with the event trait
* that targets a blob or structure.
*/
public final class EventPayloadTrait extends BooleanTrait {
public final class EventPayloadTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#eventPayload");
public EventPayloadTrait(SourceLocation sourceLocation) {
@ -35,7 +35,7 @@ public final class EventPayloadTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<EventPayloadTrait> {
public static final class Provider extends AnnotationTrait.Provider<EventPayloadTrait> {
public Provider() {
super(ID, EventPayloadTrait::new);
}

View File

@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* Binds an input member to a label in the hostPrefix of an endpoint
* trait on an operation.
*/
public final class HostLabelTrait extends BooleanTrait {
public final class HostLabelTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#hostLabel");
public HostLabelTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class HostLabelTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<HostLabelTrait> {
public static final class Provider extends AnnotationTrait.Provider<HostLabelTrait> {
public Provider() {
super(ID, HostLabelTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* An auth scheme trait uses HTTP basic auth.
*/
public final class HttpBasicAuthTrait extends BooleanTrait {
public final class HttpBasicAuthTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#httpBasicAuth");
@ -33,7 +33,7 @@ public final class HttpBasicAuthTrait extends BooleanTrait {
super(ID, sourceLocation);
}
public static final class Provider extends BooleanTrait.Provider<HttpBasicAuthTrait> {
public static final class Provider extends AnnotationTrait.Provider<HttpBasicAuthTrait> {
public Provider() {
super(ID, HttpBasicAuthTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* An auth scheme trait uses HTTP bearer auth.
*/
public final class HttpBearerAuthTrait extends BooleanTrait {
public final class HttpBearerAuthTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#httpBearerAuth");
@ -33,7 +33,7 @@ public final class HttpBearerAuthTrait extends BooleanTrait {
super(ID, sourceLocation);
}
public static final class Provider extends BooleanTrait.Provider<HttpBearerAuthTrait> {
public static final class Provider extends AnnotationTrait.Provider<HttpBearerAuthTrait> {
public Provider() {
super(ID, HttpBearerAuthTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* An auth scheme trait uses HTTP digest auth.
*/
public final class HttpDigestAuthTrait extends BooleanTrait {
public final class HttpDigestAuthTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#httpDigestAuth");
@ -33,7 +33,7 @@ public final class HttpDigestAuthTrait extends BooleanTrait {
super(ID, sourceLocation);
}
public static final class Provider extends BooleanTrait.Provider<HttpDigestAuthTrait> {
public static final class Provider extends AnnotationTrait.Provider<HttpDigestAuthTrait> {
public Provider() {
super(ID, HttpDigestAuthTrait::new);
}

View File

@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* Binds a member to a URI label of an input of an operation using
* the member name.
*/
public final class HttpLabelTrait extends BooleanTrait {
public final class HttpLabelTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#httpLabel");
public HttpLabelTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class HttpLabelTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<HttpLabelTrait> {
public static final class Provider extends AnnotationTrait.Provider<HttpLabelTrait> {
public Provider() {
super(ID, HttpLabelTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Binds a single structure member to the payload of an HTTP request.
*/
public final class HttpPayloadTrait extends BooleanTrait {
public final class HttpPayloadTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#httpPayload");
public HttpPayloadTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class HttpPayloadTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<HttpPayloadTrait> {
public static final class Provider extends AnnotationTrait.Provider<HttpPayloadTrait> {
public Provider() {
super(ID, HttpPayloadTrait::new);
}

View File

@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* Defines an operation input member that is used to prevent
* replayed requests.
*/
public final class IdempotencyTokenTrait extends BooleanTrait {
public final class IdempotencyTokenTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#idempotencyToken");
public IdempotencyTokenTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class IdempotencyTokenTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<IdempotencyTokenTrait> {
public static final class Provider extends AnnotationTrait.Provider<IdempotencyTokenTrait> {
public Provider() {
super(ID, IdempotencyTokenTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that an operation is idempotent.
*/
public final class IdempotentTrait extends BooleanTrait {
public final class IdempotentTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#idempotent");
public IdempotentTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class IdempotentTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<IdempotentTrait> {
public static final class Provider extends AnnotationTrait.Provider<IdempotentTrait> {
public Provider() {
super(ID, IdempotentTrait::new);
}

View File

@ -23,7 +23,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* can only be used to create a resource and cannot replace
* an existing resource.
*/
public final class NoReplaceTrait extends BooleanTrait {
public final class NoReplaceTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#noReplace");
public NoReplaceTrait(SourceLocation sourceLocation) {
@ -34,7 +34,7 @@ public final class NoReplaceTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<NoReplaceTrait> {
public static final class Provider extends AnnotationTrait.Provider<NoReplaceTrait> {
public Provider() {
super(ID, NoReplaceTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that an operation / service supports unauthenticated access.
*/
public final class OptionalAuthTrait extends BooleanTrait {
public final class OptionalAuthTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#optionalAuth");
public OptionalAuthTrait() {
@ -32,7 +32,7 @@ public final class OptionalAuthTrait extends BooleanTrait {
super(ID, sourceLocation);
}
public static final class Provider extends BooleanTrait.Provider<OptionalAuthTrait> {
public static final class Provider extends AnnotationTrait.Provider<OptionalAuthTrait> {
public Provider() {
super(ID, OptionalAuthTrait::new);
}

View File

@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* Indicates that a shape cannot be targeted outside of the namespace in
* which it was defined.
*/
public final class PrivateTrait extends BooleanTrait {
public final class PrivateTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#private");
public PrivateTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class PrivateTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<PrivateTrait> {
public static final class Provider extends AnnotationTrait.Provider<PrivateTrait> {
public Provider() {
super(ID, PrivateTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that an operation is read-only.
*/
public final class ReadonlyTrait extends BooleanTrait {
public final class ReadonlyTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#readonly");
public ReadonlyTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class ReadonlyTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<ReadonlyTrait> {
public static final class Provider extends AnnotationTrait.Provider<ReadonlyTrait> {
public Provider() {
super(ID, ReadonlyTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that a structure member is required.
*/
public final class RequiredTrait extends BooleanTrait {
public final class RequiredTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#required");
public RequiredTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class RequiredTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<RequiredTrait> {
public static final class Provider extends AnnotationTrait.Provider<RequiredTrait> {
public Provider() {
super(ID, RequiredTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that the streaming blob must be finite and has a known size.
*/
public final class RequiresLengthTrait extends BooleanTrait {
public final class RequiresLengthTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#requiresLength");
public RequiresLengthTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class RequiresLengthTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<RequiresLengthTrait> {
public static final class Provider extends AnnotationTrait.Provider<RequiresLengthTrait> {
public Provider() {
super(ID, RequiresLengthTrait::new);
}

View File

@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* Indicates that the data stored in the shape or member is sensitive and
* should be handled with care.
*/
public final class SensitiveTrait extends BooleanTrait {
public final class SensitiveTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#sensitive");
public SensitiveTrait(SourceLocation sourceLocation) {
@ -33,7 +33,7 @@ public final class SensitiveTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<SensitiveTrait> {
public static final class Provider extends AnnotationTrait.Provider<SensitiveTrait> {
public Provider() {
super(ID, SensitiveTrait::new);
}

View File

@ -26,7 +26,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
* not be stored in memory, or that the size of the data stored in the
* shape is unknown at the start of a request.
*/
public final class StreamingTrait extends BooleanTrait {
public final class StreamingTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#streaming");
public StreamingTrait(SourceLocation sourceLocation) {
@ -37,7 +37,7 @@ public final class StreamingTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<StreamingTrait> {
public static final class Provider extends AnnotationTrait.Provider<StreamingTrait> {
public Provider() {
super(ID, StreamingTrait::new);
}

View File

@ -15,18 +15,12 @@
package software.amazon.smithy.model.traits;
import java.util.Collections;
import java.util.stream.Stream;
import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.loader.Prelude;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.BooleanNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.node.ToNode;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.ShapeType;
import software.amazon.smithy.model.shapes.ToShapeId;
import software.amazon.smithy.model.validation.Validator;
import software.amazon.smithy.utils.OptionalUtils;
@ -130,45 +124,4 @@ public interface Trait extends FromSourceLocation, ToNode, ToShapeId {
static String makeAbsoluteName(String traitName, String defaultNamespace) {
return traitName.contains("#") ? traitName : defaultNamespace + "#" + traitName;
}
/**
* Coerces a trait value for the given type.
*
* <p>Null values provided for traits are coerced in some cases to fit
* the type referenced by the shape. This is used in the .smithy format
* to make is so that you can write "@foo" rather than "@foo(true)",
* "@foo()", or "@foo([])".
*
* <ul>
* <li>Boolean traits are converted to `true`.</li>
* <li>Structure and map traits are converted to an empty object.</li>
* <li>List and set traits are converted to an empty array.</li>
* </ul>
*
* Boolean values can be coerced from `true` to an empty object if the
* shape targeted by the trait is a structure. This allows traits to
* evolve over time from being an annotation trait to a structured
* trait (with optional members only to make it backward-compatible).
*
* @param value Value to coerce.
* @param targetType Shape Type to convert into.
* @return Returns the coerced value.
*/
static Node coerceTraitValue(Node value, ShapeType targetType) {
if (value.isNullNode()) {
if (targetType == null) {
return new BooleanNode(true, value.getSourceLocation());
} else if (targetType == ShapeType.STRUCTURE || targetType == ShapeType.MAP) {
return new ObjectNode(Collections.emptyMap(), value.getSourceLocation());
} else if (targetType == ShapeType.LIST || targetType == ShapeType.SET) {
return new ArrayNode(Collections.emptyList(), value.getSourceLocation());
}
} else if (targetType == ShapeType.STRUCTURE && value.asBooleanNode()
.filter(BooleanNode::getValue)
.isPresent()) {
return new ObjectNode(Collections.emptyMap(), value.getSourceLocation());
}
return value;
}
}

View File

@ -28,7 +28,6 @@ import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.selector.Selector;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.ShapeType;
import software.amazon.smithy.model.shapes.ToShapeId;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.ToSmithyBuilder;
@ -200,7 +199,10 @@ public final class TraitDefinition extends AbstractTrait implements ToSmithyBuil
public TraitDefinition createTrait(ShapeId target, Node value) {
// The handling of a trait definition is special-cased, so coercion
// from a null value to an object is required.
ObjectNode members = Trait.coerceTraitValue(value, ShapeType.STRUCTURE).expectObjectNode();
ObjectNode members = value.isNullNode()
? Node.objectNode()
: value.expectObjectNode();
Builder builder = builder().sourceLocation(value);
members.getMember(TraitDefinition.SELECTOR_KEY)

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Indicates that the members of a list must be unique.
*/
public final class UniqueItemsTrait extends BooleanTrait {
public final class UniqueItemsTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#uniqueItems");
public UniqueItemsTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class UniqueItemsTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<UniqueItemsTrait> {
public static final class Provider extends AnnotationTrait.Provider<UniqueItemsTrait> {
public Provider() {
super(ID, UniqueItemsTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Marks a shape as unstable.
*/
public final class UnstableTrait extends BooleanTrait {
public final class UnstableTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#unstable");
public UnstableTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class UnstableTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<UnstableTrait> {
public static final class Provider extends AnnotationTrait.Provider<UnstableTrait> {
public Provider() {
super(ID, UnstableTrait::new);
}

View File

@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.ShapeId;
/**
* Marks a structure member to be serialized to/from an XML attribute.
*/
public final class XmlAttributeTrait extends BooleanTrait {
public final class XmlAttributeTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#xmlAttribute");
public XmlAttributeTrait(SourceLocation sourceLocation) {
@ -32,7 +32,7 @@ public final class XmlAttributeTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<XmlAttributeTrait> {
public static final class Provider extends AnnotationTrait.Provider<XmlAttributeTrait> {
public Provider() {
super(ID, XmlAttributeTrait::new);
}

View File

@ -18,7 +18,7 @@ package software.amazon.smithy.model.traits;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.shapes.ShapeId;
public class XmlFlattenedTrait extends BooleanTrait {
public class XmlFlattenedTrait extends AnnotationTrait {
public static final ShapeId ID = ShapeId.from("smithy.api#xmlFlattened");
public XmlFlattenedTrait(SourceLocation sourceLocation) {
@ -29,7 +29,7 @@ public class XmlFlattenedTrait extends BooleanTrait {
this(SourceLocation.NONE);
}
public static final class Provider extends BooleanTrait.Provider<XmlFlattenedTrait> {
public static final class Provider extends AnnotationTrait.Provider<XmlFlattenedTrait> {
public Provider() {
super(ID, XmlFlattenedTrait::new);
}

View File

@ -18,7 +18,6 @@ package software.amazon.smithy.model.validation.validators;
import java.util.List;
import java.util.stream.Collectors;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.traits.Trait;
@ -53,11 +52,9 @@ public final class TraitValueValidator implements Validator {
}
Shape schema = model.getShape(shape).get();
Node coerced = Trait.coerceTraitValue(trait.toNode(), schema.getType());
NodeValidationVisitor cases = NodeValidationVisitor.builder()
.model(model)
.value(coerced)
.value(trait.toNode())
.eventShapeId(targetShape.getId())
.eventId(NAME)
.startingContext("Error validating trait `" + Trait.getIdiomaticTraitName(trait) + "`")

View File

@ -21,7 +21,6 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.net.URL;
import java.util.HashMap;
@ -38,11 +37,8 @@ import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.DynamicTrait;
import software.amazon.smithy.model.traits.ReferencesTrait;
import software.amazon.smithy.model.traits.TagsTrait;
import software.amazon.smithy.model.traits.TraitDefinition;
import software.amazon.smithy.model.traits.TraitFactory;
import software.amazon.smithy.model.validation.ValidationEvent;
@ -155,61 +151,6 @@ public class LoaderVisitorTest {
assertThat(events, not(empty()));
}
@Test
public void coercesNullTraitValues() {
Model model = Model.assembler()
.addImport(getClass().getResource("null-coerce-traits.json"))
.assemble()
.unwrap();
Shape shape = model.expectShape(ShapeId.from("ns.foo#Foo"));
assertTrue(shape.getTrait(DeprecatedTrait.class).isPresent());
assertTrue(shape.getTrait(TagsTrait.class).isPresent());
assertTrue(shape.getTrait(ReferencesTrait.class).isPresent());
}
@Test
public void coercesBooleanToStructureTraitValues() {
Model model = Model.assembler()
.addUnparsedModel("test.smithy", "namespace smithy.example\n"
+ "@foo(true)\n"
+ "string MyString\n"
+ "@trait(selector: \"*\")\n"
+ "structure foo {}\n")
.assemble()
.unwrap();
Shape shape = model.expectShape(ShapeId.from("smithy.example#MyString"));
assertTrue(shape.hasTrait("smithy.example#foo"));
}
private static Model createCoercionModel(String traitType) {
return Model.assembler()
.addUnparsedModel("test.smithy", "namespace smithy.example\n"
+ "@foo\n"
+ "string MyString\n"
+ "@trait(selector: \"*\")"
+ traitType + "\n")
.assemble()
.unwrap();
}
@Test
public void coercesListTraitValues() {
Model model = createCoercionModel("list foo { member: String }");
Shape shape = model.expectShape(ShapeId.from("smithy.example#MyString"));
assertTrue(shape.hasTrait("smithy.example#foo"));
}
@Test
public void coercesBooleanTraitValuesToStructures() {
Model model = createCoercionModel("structure foo {}");
Shape shape = model.expectShape(ShapeId.from("smithy.example#MyString"));
assertTrue(shape.hasTrait("smithy.example#foo"));
}
@Test
public void supportsCustomProperties() {
Map<String, Object> properties = MapUtils.of("a", true, "b", new HashMap<>());

View File

@ -0,0 +1,34 @@
package software.amazon.smithy.model.traits;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import java.util.Collections;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.ExpectationNotMetException;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
public class AnnotationTraitTest {
@Test
public void loadsAnnotationTraitObjects() {
SourceLocation sourceLocation = new SourceLocation("/foo");
ObjectNode objectNode = new ObjectNode(Collections.emptyMap(), sourceLocation);
SensitiveTrait trait = new SensitiveTrait.Provider().createTrait(ShapeId.from("foo#bar"), objectNode);
assertThat(trait.getSourceLocation(), equalTo(sourceLocation));
}
@Test
public void validatesAnnotationTraits() {
SourceLocation sourceLocation = new SourceLocation("/foo");
ArrayNode arrayNode = new ArrayNode(Collections.emptyList(), sourceLocation);
Assertions.assertThrows(ExpectationNotMetException.class, () -> {
new SensitiveTrait.Provider().createTrait(ShapeId.from("foo#bar"), arrayNode);
});
}
}

View File

@ -30,10 +30,10 @@ public class IdempotencyTokenTraitTest {
public void loadsTrait() {
TraitFactory provider = TraitFactory.createServiceFactory();
Optional<Trait> trait = provider.createTrait(
ShapeId.from("smithy.api#idempotencyToken"), ShapeId.from("ns.qux#foo"), Node.from(true));
ShapeId.from("smithy.api#idempotencyToken"), ShapeId.from("ns.qux#foo"), Node.objectNode());
assertTrue(trait.isPresent());
assertThat(trait.get(), instanceOf(IdempotencyTokenTrait.class));
assertThat(trait.get().toNode(), equalTo(Node.from(true)));
assertThat(trait.get().toNode(), equalTo(Node.objectNode()));
}
}

Some files were not shown because too many files have changed in this diff Show More