Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.10] Add protocol and auth trait requirements #280

Merged
merged 2 commits into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions docs/source/spec/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4675,7 +4675,21 @@ Summary
Trait selector
``[trait|trait]``
Value type
Annotation trait.
An object with the following properties:

.. list-table::
:header-rows: 1
:widths: 10 23 67

* - Property
- Type
- Description
* - traits
- [:ref:`shape-id`]
- List of shape IDs that protocol implementations MUST understand
in order to successfully use the protocol. Each shape MUST exist
and MUST be a trait. Code generators SHOULD ensure that they
support each listed trait.

Smithy is protocol agnostic, which means it focuses on the interfaces and
abstractions that are provided to end-users rather than how the data is sent
Expand Down Expand Up @@ -4726,7 +4740,7 @@ The following example defines a service that supports both the hypothetical
"type": "structure",
"traits": {
"smithy.api#documentation": "An example JSON protocol."
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {},
"smithy.api#trait": {
"selector": "service"
}
Expand All @@ -4736,7 +4750,7 @@ The following example defines a service that supports both the hypothetical
"type": "structure",
"traits": {
"smithy.api#documentation": "An example JSON protocol."
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {},
"smithy.api#trait": {
"selector": "service"
}
Expand Down Expand Up @@ -4928,7 +4942,21 @@ Summary
Trait selector
``[trait|trait]``
Value type
Annotation trait.
An object with the following properties:
mtdowling marked this conversation as resolved.
Show resolved Hide resolved

.. list-table::
:header-rows: 1
:widths: 10 23 67

* - Property
- Type
- Description
* - traits
- [:ref:`shape-id`]
- List of shape IDs that auth scheme implementations MUST
understand in order to successfully use the scheme. Each shape
MUST exist and MUST be a trait. Code generators SHOULD ensure
that they support each listed trait.

Every operation in the closure of a service is expected to support the
authentication schemes applied to a service unless the service or operation
Expand Down Expand Up @@ -4970,7 +4998,7 @@ and the hypothetical ``fooExample`` authentication scheme.
"smithy.example#fooExample": {
"type": "structure",
"traits": {
"smithy.api#authDefinition": true,
"smithy.api#authDefinition": {},
"smithy.api#trait": {
"selector": "service"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#authDefinition": true,
"smithy.api#authDefinition": {
"traits": [
"aws.auth#unsignedPayload"
]
},
"smithy.api#documentation": "Signature Version 4 is the process to add authentication information to AWS requests sent by HTTP. For security, most requests to AWS must be signed with an access key, which consists of an access key ID and secret access key. These two keys are commonly referred to as your security credentials.",
"smithy.api#externalDocumentation": "https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,17 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"smithy.api#httpError",
"smithy.api#httpHeader",
"smithy.api#httpLabel",
"smithy.api#httpPayload",
"smithy.api#httpPrefixHeaders",
"smithy.api#httpQuery",
"smithy.api#jsonName"
]
},
"smithy.api#documentation": "A RESTful protocol that sends JSON in structured payloads."
}
},
Expand All @@ -33,7 +43,20 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"smithy.api#httpError",
"smithy.api#httpHeader",
"smithy.api#httpLabel",
"smithy.api#httpPayload",
"smithy.api#httpPrefixHeaders",
"smithy.api#httpQuery",
"smithy.api#xmlAttribute",
"smithy.api#xmlFlattened",
"smithy.api#xmlName",
"smithy.api#xmlNamespace"
]
},
"smithy.api#documentation": "A RESTful protocol that sends XML in structured payloads.",
"smithy.api#deprecated": true
}
Expand All @@ -52,7 +75,11 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"smithy.api#jsonName"
]
},
"smithy.api#documentation": "An RPC-based protocol that sends JSON payloads. This protocol does not use HTTP binding traits."
}
},
Expand All @@ -70,7 +97,11 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"smithy.api#jsonName"
]
},
"smithy.api#documentation": "An RPC-based protocol that sends JSON payloads. This protocol does not use HTTP binding traits."
}
},
Expand All @@ -80,7 +111,14 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"smithy.api#xmlAttribute",
"smithy.api#xmlFlattened",
"smithy.api#xmlName",
"smithy.api#xmlNamespace"
]
},
"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
}
Expand All @@ -91,7 +129,15 @@
"smithy.api#trait": {
"selector": "service"
},
"smithy.api#protocolDefinition": true,
"smithy.api#protocolDefinition": {
"traits": [
"aws.protocols#ec2QueryName",
"smithy.api#xmlAttribute",
"smithy.api#xmlFlattened",
"smithy.api#xmlName",
"smithy.api#xmlNamespace"
]
},
"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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,96 @@

package software.amazon.smithy.model.traits;

import software.amazon.smithy.model.SourceLocation;
import java.util.ArrayList;
import java.util.List;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* This trait is attached to another trait to define an auth scheme.
*/
public final class AuthDefinitionTrait extends BooleanTrait {
public final class AuthDefinitionTrait extends AbstractTrait implements ToSmithyBuilder<AuthDefinitionTrait> {

public static final ShapeId ID = ShapeId.from("smithy.api#authDefinition");
private final List<ShapeId> traits;

public AuthDefinitionTrait(SourceLocation sourceLocation) {
super(ID, sourceLocation);
public AuthDefinitionTrait(Builder builder) {
super(ID, builder.getSourceLocation());
traits = ListUtils.copyOf(builder.traits);
}

public AuthDefinitionTrait() {
this(SourceLocation.NONE);
/**
* Gets the list of shape IDs that auth implementations must know about
* in order to successfully utilize the auth scheme.
*
* @return Returns the auth traits.
*/
public List<ShapeId> getTraits() {
return traits;
}

public static final class Provider extends BooleanTrait.Provider<AuthDefinitionTrait> {
public static Builder builder() {
return new Builder();
}

@Override
protected Node createNode() {
if (traits.isEmpty()) {
return Node.objectNode();
}

ArrayNode ids = traits.stream()
.map(ShapeId::toString)
.map(Node::from)
.collect(ArrayNode.collect());

return Node.objectNode().withMember("traits", ids);
}

@Override
public Builder toBuilder() {
return builder().sourceLocation(getSourceLocation()).traits(traits);
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID, AuthDefinitionTrait::new);
super(ID);
}

@Override
public AuthDefinitionTrait createTrait(ShapeId target, Node value) {
Builder builder = builder().sourceLocation(value);
ObjectNode objectNode = value.expectObjectNode();
objectNode.getArrayMember("traits").ifPresent(traits -> {
for (String string : Node.loadArrayOfString("traits", traits)) {
builder.addTrait(ShapeId.from(string));
}
});
return builder.build();
}
}

public static final class Builder extends AbstractTraitBuilder<AuthDefinitionTrait, Builder> {
private final List<ShapeId> traits = new ArrayList<>();

@Override
public AuthDefinitionTrait build() {
return new AuthDefinitionTrait(this);
}

public Builder traits(List<ShapeId> traits) {
this.traits.clear();
this.traits.addAll(traits);
return this;
}

public Builder addTrait(ShapeId trait) {
traits.add(trait);
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,96 @@

package software.amazon.smithy.model.traits;

import software.amazon.smithy.model.SourceLocation;
import java.util.ArrayList;
import java.util.List;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* A trait that is attached to other traits to define a Smithy protocol.
*/
public final class ProtocolDefinitionTrait extends BooleanTrait {
public final class ProtocolDefinitionTrait extends AbstractTrait implements ToSmithyBuilder<ProtocolDefinitionTrait> {

public static final ShapeId ID = ShapeId.from("smithy.api#protocolDefinition");
private final List<ShapeId> traits;

public ProtocolDefinitionTrait(SourceLocation sourceLocation) {
super(ID, sourceLocation);
public ProtocolDefinitionTrait(Builder builder) {
super(ID, builder.getSourceLocation());
traits = ListUtils.copyOf(builder.traits);
}

public ProtocolDefinitionTrait() {
this(SourceLocation.NONE);
/**
* Gets the list of shape IDs that protocol implementations must know about
* in order to successfully utilize the protocol.
*
* @return Returns the protocol traits.
*/
public List<ShapeId> getTraits() {
return traits;
}

public static final class Provider extends BooleanTrait.Provider<ProtocolDefinitionTrait> {
public static Builder builder() {
return new Builder();
}

@Override
protected Node createNode() {
if (traits.isEmpty()) {
return Node.objectNode();
}

ArrayNode ids = traits.stream()
.map(ShapeId::toString)
.map(Node::from)
.collect(ArrayNode.collect());

return Node.objectNode().withMember("traits", ids);
}

@Override
public Builder toBuilder() {
return builder().sourceLocation(getSourceLocation()).traits(traits);
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID, ProtocolDefinitionTrait::new);
super(ID);
}

@Override
public ProtocolDefinitionTrait createTrait(ShapeId target, Node value) {
Builder builder = builder().sourceLocation(value);
ObjectNode objectNode = value.expectObjectNode();
objectNode.getArrayMember("traits").ifPresent(traits -> {
for (String string : Node.loadArrayOfString("traits", traits)) {
builder.addTrait(ShapeId.from(string));
}
});
return builder.build();
}
}

public static final class Builder extends AbstractTraitBuilder<ProtocolDefinitionTrait, Builder> {
private final List<ShapeId> traits = new ArrayList<>();

@Override
public ProtocolDefinitionTrait build() {
return new ProtocolDefinitionTrait(this);
}

public Builder traits(List<ShapeId> traits) {
this.traits.clear();
this.traits.addAll(traits);
return this;
}

public Builder addTrait(ShapeId trait) {
traits.add(trait);
return this;
}
}
}
Loading