Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ alloy.UrlFormNameTrait$Provider
alloy.UncheckedExamplesTrait$Provider
alloy.UntaggedUnionTrait$Provider
alloy.UuidFormatTrait$Provider
alloy.HttpPolymorphicResponseTrait$Provider
alloy.HttpSuccessTrait$Provider
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ alloy.validation.DiscriminatedUnionValidator
alloy.validation.SimpleRestJsonHttpHeaderValidator
alloy.validation.SimpleRestJsonValidator
alloy.validation.StructurePatternTraitValidator
alloy.validation.HttpPolymorphicResponseValidator
12 changes: 12 additions & 0 deletions modules/core/resources/META-INF/smithy/http.smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$version: "2"

namespace alloy

@trait(
selector: "structure > member[trait|required] :test(> :test(union))",
structurallyExclusive: "member"
)
structure httpPolymorphicResponse {}

@trait(selector: "structure")
integer httpSuccess
1 change: 1 addition & 0 deletions modules/core/resources/META-INF/smithy/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ unions.smithy
urlform.smithy
uuid.smithy
metadata.smithy
http.smithy
1 change: 1 addition & 0 deletions modules/core/resources/META-INF/smithy/uuid.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ structure uuidFormat {}

@uuidFormat
string UUID

46 changes: 46 additions & 0 deletions modules/core/src/alloy/HttpPolymorphicResponseTrait.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright 2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 alloy;

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.model.traits.AnnotationTrait;
import software.amazon.smithy.model.traits.AbstractTrait;

public class HttpPolymorphicResponseTrait extends AnnotationTrait {

public static ShapeId ID = ShapeId.from("alloy#httpPolymorphicResponse");

public HttpPolymorphicResponseTrait(ObjectNode node) {
super(ID, node);
}

public HttpPolymorphicResponseTrait() {
super(ID, Node.objectNode());
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID);
}

@Override
public HttpPolymorphicResponseTrait createTrait(ShapeId target, Node node) {
return new HttpPolymorphicResponseTrait(node.expectObjectNode());
}
}
}
59 changes: 59 additions & 0 deletions modules/core/src/alloy/HttpSuccessTrait.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* Copyright 2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 alloy;

import software.amazon.smithy.model.FromSourceLocation;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.NumberNode;
import software.amazon.smithy.model.traits.AbstractTrait;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.model.shapes.ShapeId;

public final class HttpSuccessTrait extends AbstractTrait {
public static final ShapeId ID = ShapeId.from("alloy#httpSuccess");

private final int code;

public HttpSuccessTrait(int code, FromSourceLocation sourceLocation) {
super(ID, sourceLocation);
this.code = code;
}

public HttpSuccessTrait(int code) {
this(code, SourceLocation.NONE);
}

public static final class Provider extends AbstractTrait.Provider {
public Provider() {
super(ID);
}

@Override
public Trait createTrait(ShapeId target, Node value) {
return new HttpSuccessTrait(value.expectNumberNode().getValue().intValue(), value.getSourceLocation());
}
}

public int getCode() {
return code;
}

@Override
protected Node createNode() {
return new NumberNode(code, getSourceLocation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Copyright 2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 alloy.validation;

import alloy.HttpPolymorphicResponseTrait;
import alloy.HttpSuccessTrait;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.*;
import software.amazon.smithy.model.traits.HttpTrait;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class HttpPolymorphicResponseValidator extends AbstractValidator {

protected static String EXPECTED_SINGLE_MEMBER = "The httpPolymorphicResponse trait can only be used in a structure that has a single required member, and this member must target a union";
protected static String EXPECTED_HTTP_SUCCESS_ON_ALL_MEMBER_TARGETS = "The members of a union targeted by httpPolymorphicResponse trait must all target a structure annotated with @alloy.httpSuccess";
protected static String EXPECTED_DISTINCT_HTTP_SUCCESS = "The targets of the members of this union must have distinct httpSuccess values";

@Override
public List<ValidationEvent> validate(Model model) {
return model.getMemberShapesWithTrait(HttpPolymorphicResponseTrait.class).stream().flatMap(structureMember -> {
StructureShape container = model.expectShape(structureMember.getContainer(), StructureShape.class);
List<ValidationEvent> errors = new ArrayList<ValidationEvent>();
if (container.getAllMembers().size() != 1) {
errors.add(error(container, EXPECTED_SINGLE_MEMBER));
}
UnionShape union = model.expectShape(structureMember.getTarget(), UnionShape.class);
union.members().stream().collect(Collectors.groupingBy(member -> {
return model.expectShape(member.getTarget()).getTrait(HttpSuccessTrait.class)
.map(httpSuccess -> httpSuccess.getCode());
})).entrySet().stream().forEach(entry -> {
if (!entry.getKey().isPresent()) {
errors.add(error(structureMember, EXPECTED_HTTP_SUCCESS_ON_ALL_MEMBER_TARGETS));
} else if (entry.getValue().size() > 1) {
Optional<Integer> statusCode = entry.getKey();
errors.add(error(union, EXPECTED_DISTINCT_HTTP_SUCCESS));
}
});

return errors.stream();
}).collect(Collectors.toList());
}
}
23 changes: 23 additions & 0 deletions modules/core/test/resources/META-INF/smithy/traits.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use alloy#untagged
use alloy#urlFormFlattened
use alloy#urlFormName
use alloy#uuidFormat
use alloy#httpSuccess
use alloy#httpPolymorphicResponse
use alloy.common#countryCodeFormat
use alloy.common#emailFormat
use alloy.common#hexColorCodeFormat
Expand Down Expand Up @@ -211,3 +213,24 @@ structure TestUrlFormName {
@urlFormName("Test")
test: String
}

operation TestHttpPolymporhicResponse {
input:= {
@httpPolymorphicResponse
@required
response: PolymporhicResponseUnion
}
}

union PolymporhicResponseUnion{
created: TestCreated
okay: TestOkay
}

@httpSuccess(201)
structure TestCreated {
}

@httpSuccess(200)
structure TestOkay {
}
Loading