7. Type refinement traits#
Type refinement traits are traits that significantly refine, or change, the type of a shape.
7.1. default
trait#
- Summary
- Provides a structure member with a default value.
- Trait selector
:is(simpleType, list, map, structure > member :test(> :is(simpleType, list, map)))
A simple type, list, map, or a member of a structure that targets a simple type, list, or map.
- Value type
- Document type.
- See also
The @default
trait assigns a default value to a structure member. The
following example defines a structure with a "language" member that has a
default value:
structure Message {
@required
title: String
language: Language = "en"
}
enum Language {
EN = "en"
}
The above example uses syntactic sugar to apply the @default
trait. The
Message
definition is exactly equivalent to:
structure Message {
@required
title: String
@default("en")
language: Language
}
The @default
trait can be added to root-level simple types, lists, or
maps. This can serve as a kind of template to enforce default values across
structure members in a model. Any structure member that targets a shape
marked with @default
MUST also add a matching @default
trait to the
member.
@default(0)
integer ZeroValueInteger
structure Message {
zeroValueInteger: ZeroValueInteger = 0 // must be repeated and match the target.
}
The @default
trait on a structure member can be set to null
to
explicitly indicate that the member has no default value or to override the default
value requirement of a targeted shape.
@default(0)
integer ZeroValueInteger
structure Message {
zeroValueInteger: ZeroValueInteger = null // forces the member to be optional
}
Note
- The
@default
trait on root-level shapes has no impact when targeted by any other shape than a structure member. - The
@default
trait on root-level shapes cannot be set tonull
. - The clientOptional trait applied to a member marked with the
default
trait causes non-authoritative generators to ignore thedefault
trait.
7.1.1. Default value constraints#
The value of the @default
trait MUST be compatible with the shape targeted
by the member and any applied constraint traits (for example, values for
numeric types MUST be numbers that fit within the targeted type and match any
range constraints, string types match any
length or pattern traits, etc).
The following shapes have restrictions on their default values:
- blob: can be set to any valid base64-encoded string.
- enum: can be set to any valid string value of the enum.
- intEnum: can be set to any valid integer value of the enum.
- document: can be set to
null
,`true
,false
, string, numbers, an empty list, or an empty map. - list: can only be set to an empty list.
- map: can only be set to an empty map.
- structure: no default value.
- union: no default value.
7.1.2. Impact on API design#
The @default
trait SHOULD NOT be used for partial updates or patch style
operations where it is necessary to differentiate between omitted values and
explicitly set values. Assigning default values is typically something that
occurs during deserialization, and as such, it is impossible for a server to
differentiate between whether a property was set to its default value or if a
property was omitted.
7.1.3. Updating default values#
The default value of a root-level shape MUST NOT be changed nor can the default trait be added or removed from an existing root-level shape. Changing the default value of a root-level shape would cause any member reference to the shape to break and could inadvertently impact code generated types for the shape.
The default value of a member SHOULD NOT be changed. However, it MAY be necessary in extreme cases to change a default value if changing the default value addresses a customer-impacting issue or availability issue for a service. Changing default values can result in parties disagreeing on the default value of a member because they are using different versions of the same model.
7.1.4. Default value serialization#
- Implementations that ignore
default
traits do not assume a default value for a member. For example, non-authoritative implementations will ignore thedefault
trait when a member is marked with the clientOptional trait. These implementations would serialize any explicitly given value, even if it happens to be the default value. - All effective default values SHOULD be serialized. This ensures that messages are unambiguous and do not change during deserialization if the default value for a member changes after the message was serialized.
- To avoid information disclosure, implementations MAY choose to not serialize a default value if the member is marked with the internal trait.
- A member marked
@required
MUST be serialized, including members that have a default.
7.2. addedDefault
trait#
- Summary
- Indicates that the default trait was added to a structure member
after initially publishing the member. This allows tooling to decide
whether to ignore the
@default
trait if it will break backward compatibility in the tool. - Trait selector
structure > member [trait|default]
Member of a structure marked with the default trait
- Value type
- Annotation trait.
- See also
7.3. required
trait#
- Summary
- Marks a structure member as required, meaning a value for the member MUST be present.
- Trait selector
structure > member
Member of a structure
- Value type
- Annotation trait.
- See also
The following example defines a structure with a required member.
structure MyStructure {
@required
foo: FooString
}
Important
The required trait isn't just for inputs
The required trait indicates that value MUST always be present for a member. It applies to all shapes, including inputs of operations, outputs of operations, and errors.
7.4. clientOptional
trait#
- Summary
- Requires that non-authoritative generators like clients treat a structure member as optional regardless of if the member is also marked with the required trait or default trait.
- Trait selector
structure > member
- Value type
- Annotation trait
- See also
For cases when a service is unsure if a member will be required forever, the
member can be marked with the @clientOptional
trait to ensure that
non-authoritative consumers of the model like clients treat the member as
optional. The @required
trait can be backward compatibly removed from a
member marked as @clientOptional
(and does not need to be replaced with
the @default
trait). This causes the @required
and @default
traits
to function only as a server-side concern.
The @required
trait on foo
in the following structure is considered a
validation constraint rather than a type refinement trait:
structure Foo {
@required
@clientOptional
foo: String
}
Note
Structure members in Smithy are automatically considered optional. For example, the following structure:
structure Foo {
baz: String
}
Is equivalent to the following structure:
structure Foo {
@clientOptional
baz: String
}
7.5. enumValue
trait#
- Summary
- Defines the value of an enum or intEnum. For enum shapes, a non-empty string value must be used. For intEnum shapes, an integer value must be used.
- Trait selector
:is(enum, intEnum) > member
- Value type
string
orinteger
$version: "2"
namespace smithy.example
enum Enum {
@enumValue("foo")
FOO
}
intEnum IntEnum {
@enumValue(1)
FOO
}
The following enum definition uses syntactic sugar that is exactly equivalent:
$version: "2"
namespace smithy.example
enum Enum {
FOO = "foo"
}
intEnum IntEnum {
FOO = 1
}
7.6. error
trait#
- Summary
- Indicates that a structure shape represents an error. All shapes referenced by the errors list of an operation MUST be targeted with this trait.
- Trait selector
structure
- Value type
string
that MUST be set to "client" or "server" to indicate if the client or server is at fault for the error.- Conflicts with
- trait
The following structure defines a throttling error.
@error("client")
structure ThrottlingError {}
Note that this structure is lacking the retryable
trait that generically
lets clients know that the error is retryable.
@error("client")
@retryable
structure ThrottlingError {}
When using an HTTP-based protocol, it is recommended to add an httpError trait to use an appropriate HTTP status code with the error.
@error("client")
@retryable
@httpError(429)
structure ThrottlingError {}
The message
member of an error structure is special-cased. It contains
the human-readable message that describes the error. If the message
member
is not defined in the structure, code generated for the error may not provide
an idiomatic way to access the error message (e.g., an exception message
in Java).
@error("client")
@retryable
@httpError(429)
structure ThrottlingError {
@required
message: String
}
7.7. input
trait#
- Summary
- Specializes a structure for use only as the input of a single operation, providing relaxed backward compatibility requirements for structure members.
- Trait selector
structure
- Value type
- Annotation trait.
- Conflicts with
- See also
The following example defines an @input
structure:
@input
structure SomeOperationInput {
@required
name: String
}
7.7.1. @input
structure constraints#
Structure shapes marked with the @input
trait MUST adhere to the
following constraints:
- They can only be referenced in the model as an operation's input.
- They cannot be used as the input of more than one operation.
- They SHOULD have a shape name that starts with the name of the
operation that targets it (if any). For example, the input shape of the
GetSprocket
operation SHOULD be namedGetSprocketInput
,GetSprocketRequest
, or something similar.
These constraints allow tooling to specialize operation input shapes in ways that would otherwise require complex model transformations.
7.7.2. Impact on backward compatibility#
Required members of a structure marked with the @input
trait are implicitly
considered clientOptional. It is backward
compatible to remove the @required
trait from top-level members of
structures marked with the @input
trait, and the @required
trait does
not need to be replaced with the @default
trait (though this is allowed
as well). This gives service teams the ability to remove the @required
trait from top-level input members and loosen requirements without risking
breaking previously generated clients.
7.8. output
trait#
- Summary
- Specializes a structure for use only as the output of a single operation.
- Trait selector
structure
- Value type
- Annotation trait.
- Conflicts with
- input trait, error trait
7.8.1. @output
structure constraints#
Structure shapes marked with the @output
trait MUST adhere to the
following constraints:
- They can only be referenced in the model as an operation's output.
- They cannot be used as the output of more than one operation.
- They SHOULD have a shape name that starts with the name of the
operation that targets it (if any). For example, the output shape of the
GetSprocket
operation SHOULD be namedGetSprocketOutput
.
These constraints allow tooling to specialize operation output shapes in ways that would otherwise require complex model transformations.
7.9. sparse
trait#
- Summary
- Indicates that lists and maps MAY contain
null
values. Thesparse
trait has no effect on map keys; map keys are never allowed to benull
. - Trait selector
:is(list, map)
- Value type
- Annotation trait.
The following example defines a list shape that MAY contain
null
values:
@sparse
list SparseList {
member: String
}
The following example defines a map shape that MAY contain
null
values:
@sparse
map SparseMap {
key: String
value: String
}
7.10. mixin
trait#
- Summary
- Indicates that the targeted shape is a mixin.
- Trait selector
:not(member)
- Value type
structure
The mixin trait is a structure that contains the following members:
Property | Type | Description |
---|---|---|
localTraits |
[Shape ID] | A list of shape IDs which MUST reference valid traits that are applied directly to the mixin. The traits in the list are not copied onto shapes that use the mixin. This only affects traits applied to the mixin container shape and has no impact on the members contained within a mixin. Note The |
@mixin
structure BaseUser {
id: String
}
structure UserDetails with [BaseUser] {
alias: String
email: String
}
See also
See Mixins for details on how mixins work.