diff --git a/docs/source/spec/core/stream-traits.rst b/docs/source/spec/core/stream-traits.rst index a58536da299..f113b1f6d76 100644 --- a/docs/source/spec/core/stream-traits.rst +++ b/docs/source/spec/core/stream-traits.rst @@ -12,6 +12,7 @@ once. This includes both streaming binary data and event streams. :local: :backlinks: none + .. _streaming-trait: ------------------- @@ -30,29 +31,15 @@ Summary Trait selector:: ``:each(blob, union)`` Value type - ``structure`` - -The value of the ``streaming`` trait is a structure that supports the following -optional members: - -.. list-table:: - :header-rows: 1 - :widths: 10 10 80 - - * - Property - - Type - - Description - * - requiresLength - - ``boolean`` - - Indicates that the stream must have a known size. - - In an HTTP-based protocol, for instance, this indicates that the - ``content-length`` header must be set. - -Shapes targeted by this trait MAY NOT be used outside of top level operation -inputs and operation outputs. Additionally, the ``streaming`` trait is -*structurally exclusive by target*, meaning only a single member of a -structure can target a shape marked as ``streaming``. + Annotation trait +Validation + * ``streaming`` shapes can only be referenced from top-level members + of operation input or output structures. + * Structures that contain a member that targets a ``streaming`` shape + MUST NOT be targeted by other members. + * The ``streaming`` trait is *structurally exclusive by target*, meaning + only a single member of a structure can target a shape marked as + ``streaming``. .. tabs:: @@ -71,6 +58,37 @@ structure can target a shape marked as ``streaming``. @streaming blob StreamingBlob + +.. _requiresLength-trait: + +------------------------ +``requiresLength`` trait +------------------------ + +Summary + Indicates that the streaming blob MUST be finite and has a known size. + + In an HTTP-based protocol, for instance, this trait indicates that the + ``Content-Length`` header MUST be sent prior to a client or server + sending the payload of a message. This can be useful for services that + need to determine if a request will be accepted based on its size or + where to store data based on the size of the stream. +Trait selector:: + ``blob[trait|streaming]`` + + *A blob shape marked with the streaming trait* +Value type + ``structure`` + +.. tabs:: + + .. code-tab:: smithy + + @streaming + @requiresLength + blob FiniteStreamingBlob + + .. _event-streams: ------------- diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java index 878f0f76533..be895329ae9 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/Prelude.java @@ -77,6 +77,7 @@ import software.amazon.smithy.model.traits.ReadonlyTrait; import software.amazon.smithy.model.traits.ReferencesTrait; import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.model.traits.RequiresLengthTrait; import software.amazon.smithy.model.traits.ResourceIdentifierTrait; import software.amazon.smithy.model.traits.RetryableTrait; import software.amazon.smithy.model.traits.SensitiveTrait; @@ -183,6 +184,7 @@ public final class Prelude { RangeTrait.ID, ReadonlyTrait.ID, ReferencesTrait.ID, + RequiresLengthTrait.ID, RequiredTrait.ID, ResourceIdentifierTrait.ID, RetryableTrait.ID, diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/traits/RequiresLengthTrait.java b/smithy-model/src/main/java/software/amazon/smithy/model/traits/RequiresLengthTrait.java new file mode 100644 index 00000000000..4c7e7a90909 --- /dev/null +++ b/smithy-model/src/main/java/software/amazon/smithy/model/traits/RequiresLengthTrait.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.model.traits; + +import software.amazon.smithy.model.SourceLocation; +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 static final ShapeId ID = ShapeId.from("smithy.api#requiresLength"); + + public RequiresLengthTrait(SourceLocation sourceLocation) { + super(ID, sourceLocation); + } + + public RequiresLengthTrait() { + this(SourceLocation.NONE); + } + + public static final class Provider extends BooleanTrait.Provider { + public Provider() { + super(ID, RequiresLengthTrait::new); + } + } +} diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/traits/StreamingTrait.java b/smithy-model/src/main/java/software/amazon/smithy/model/traits/StreamingTrait.java index 8e32ce0ba97..6aa2d4828e5 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/traits/StreamingTrait.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/traits/StreamingTrait.java @@ -16,43 +16,31 @@ package software.amazon.smithy.model.traits; import software.amazon.smithy.model.Model; -import software.amazon.smithy.model.node.Node; -import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.SourceLocation; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.utils.ToSmithyBuilder; /** * Indicates that the the data stored in the shape is very large and should * 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 AbstractTrait implements ToSmithyBuilder { +public final class StreamingTrait extends BooleanTrait { public static final ShapeId ID = ShapeId.from("smithy.api#streaming"); - private static final String REQUIRES_LENGTH = "requiresLength"; - private final boolean requiresLength; - - private StreamingTrait(Builder builder) { - super(ID, builder.getSourceLocation()); - requiresLength = builder.requiresLength; + public StreamingTrait(SourceLocation sourceLocation) { + super(ID, sourceLocation); } - /** - * Creates a builder for a streaming trait. - * - * @return Returns the created builder. - */ - public static Builder builder() { - return new Builder(); + public StreamingTrait() { + this(SourceLocation.NONE); } - /** - * @return Returns true if the stream requires a known length. - */ - public boolean getRequiresLength() { - return requiresLength; + public static final class Provider extends BooleanTrait.Provider { + public Provider() { + super(ID, StreamingTrait::new); + } } /** @@ -75,52 +63,4 @@ public static boolean isEventStream(Shape shape) { public static boolean isEventStream(Model model, MemberShape member) { return isEventStream(model.expectShape(member.getTarget())); } - - @Override - public Builder toBuilder() { - return builder().requiresLength(requiresLength); - } - - @Override - protected Node createNode() { - return requiresLength ? Node.objectNode().withMember(REQUIRES_LENGTH, true) : Node.objectNode(); - } - - public static final class Provider implements TraitService { - @Override - public ShapeId getShapeId() { - return ID; - } - - @Override - public StreamingTrait createTrait(ShapeId target, Node value) { - ObjectNode node = value.expectObjectNode(); - return builder().requiresLength(node.getBooleanMemberOrDefault(REQUIRES_LENGTH)).build(); - } - } - - /** - * Builds a {@link StreamingTrait} trait. - */ - public static final class Builder extends AbstractTraitBuilder { - private boolean requiresLength; - - private Builder() {} - - @Override - public StreamingTrait build() { - return new StreamingTrait(this); - } - - /** - * Indicates if the length of the stream must be known. - * - * @param requiresLength Set to true to require a known length. - * @return Returns the builder. - */ - public Builder requiresLength(boolean requiresLength) { - this.requiresLength = requiresLength; - return this; - } - } } diff --git a/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService b/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService index 501817f5c57..cfe2361f031 100644 --- a/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService +++ b/smithy-model/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService @@ -32,6 +32,7 @@ software.amazon.smithy.model.traits.RangeTrait$Provider software.amazon.smithy.model.traits.ReadonlyTrait$Provider software.amazon.smithy.model.traits.ReferencesTrait$Provider software.amazon.smithy.model.traits.RequiredTrait$Provider +software.amazon.smithy.model.traits.RequiresLengthTrait$Provider software.amazon.smithy.model.traits.ResourceIdentifierTrait$Provider software.amazon.smithy.model.traits.RetryableTrait$Provider software.amazon.smithy.model.traits.SensitiveTrait$Provider diff --git a/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude-traits.smithy b/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude-traits.smithy index 3dbb51dae46..0b0572e446f 100644 --- a/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude-traits.smithy +++ b/smithy-model/src/main/resources/software/amazon/smithy/model/loader/prelude-traits.smithy @@ -315,16 +315,18 @@ structure sensitive {} @trait string since -/// Indicates that the the data stored in the shape is very large and should not +/// Indicates that the data stored in the shape is very large and should not /// be stored in memory, or that the size of the data stored in the shape is /// unknown at the start of a request. If the target is a union then the shape /// represents a stream of events. @trait(selector: ":each(blob, union)", structurallyExclusive: "target") @tags(["diff.error.const"]) -structure streaming { - /// Indicates that the stream must have a known size. - requiresLength: Boolean, -} +structure streaming {} + +/// Indicates that the streaming blob must be finite and has a known size. +@trait(selector: "blob[trait|streaming]") +@tags(["diff.error.const"]) +structure requiresLength {} /// Tags a shape with arbitrary tag names that can be used to filter and /// group shapes in the model. diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/traits/StreamingTraitTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/traits/StreamingTraitTest.java index 152e2036a1a..10fa2335929 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/traits/StreamingTraitTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/traits/StreamingTraitTest.java @@ -34,6 +34,6 @@ public void loadsTrait() { assertTrue(trait.isPresent()); assertThat(trait.get(), instanceOf(StreamingTrait.class)); - assertThat(trait.get().toNode(), equalTo(Node.objectNode())); + assertThat(trait.get().toNode(), equalTo(Node.from(true))); } }