Skip to content

Commit

Permalink
Make requiresLength a separate trait
Browse files Browse the repository at this point in the history
Using `requiresLength` as a property of the `streaming` trait no longer
makes sense because event streams should virtually never have a known or
finite length before they begin.
  • Loading branch information
mtdowling committed Apr 13, 2020
1 parent c8c4a1a commit eafe510
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 99 deletions.
64 changes: 41 additions & 23 deletions docs/source/spec/core/stream-traits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ once. This includes both streaming binary data and event streams.
:local:
:backlinks: none


.. _streaming-trait:

-------------------
Expand All @@ -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::

Expand All @@ -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:

-------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -183,6 +184,7 @@ public final class Prelude {
RangeTrait.ID,
ReadonlyTrait.ID,
ReferencesTrait.ID,
RequiresLengthTrait.ID,
RequiredTrait.ID,
ResourceIdentifierTrait.ID,
RetryableTrait.ID,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<RequiresLengthTrait> {
public Provider() {
super(ID, RequiresLengthTrait::new);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<StreamingTrait> {
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<StreamingTrait> {
public Provider() {
super(ID, StreamingTrait::new);
}
}

/**
Expand All @@ -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<StreamingTrait, Builder> {
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
}

0 comments on commit eafe510

Please sign in to comment.