-
Notifications
You must be signed in to change notification settings - Fork 218
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
Add endpoint discovery traits #165
Merged
JordonPhillips
merged 15 commits into
smithy-lang:master
from
JordonPhillips:endpoint-discovery
Sep 30, 2019
Merged
Changes from 11 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
cb6837a
Add endpoint discovery skeleton
JordonPhillips 18eaeb1
Add endpoint discovery trait tests
JordonPhillips a996eac
Add endpoint discovery knowledge index
JordonPhillips da63263
Add discovery index tests
JordonPhillips cdf5ea2
Add endpoint discovery validators
JordonPhillips dcf8fed
Add endpoint discovery validator tests
JordonPhillips 1330e5d
Add endpoint operation validation
JordonPhillips 4727675
document endpoint discovery
JordonPhillips 1fc1c1e
Add transformer plugin for endpoint discovery
JordonPhillips 7623169
fix checkstyle errors
JordonPhillips 1aeca21
Reformat endpoint discovery code
JordonPhillips a270310
Remove discoveredEndpoint trait if it's optional
JordonPhillips 11f026f
Rename endpoint discovery traits
JordonPhillips 0ee61cc
String endpoint discovery id when cleaning
JordonPhillips 914f26d
Update discovery names in docs
JordonPhillips File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
...a/software/amazon/smithy/aws/traits/endpointdiscovery/CleanDiscoveryTraitTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2019 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.aws.traits.endpointdiscovery; | ||
|
||
import java.util.Collection; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import software.amazon.smithy.model.Model; | ||
import software.amazon.smithy.model.shapes.ServiceShape; | ||
import software.amazon.smithy.model.shapes.Shape; | ||
import software.amazon.smithy.model.shapes.ShapeId; | ||
import software.amazon.smithy.model.traits.ErrorTrait; | ||
import software.amazon.smithy.model.traits.Trait; | ||
import software.amazon.smithy.model.transform.ModelTransformer; | ||
import software.amazon.smithy.model.transform.ModelTransformerPlugin; | ||
|
||
/** | ||
* Removes the endpoint discovery trait from a service if the referenced operation or error are removed. | ||
*/ | ||
public class CleanDiscoveryTraitTransformer implements ModelTransformerPlugin { | ||
@Override | ||
public Model onRemove(ModelTransformer transformer, Collection<Shape> shapes, Model model) { | ||
Set<ShapeId> removedOperations = shapes.stream() | ||
.filter(Shape::isOperationShape) | ||
.map(Shape::getId) | ||
.collect(Collectors.toSet()); | ||
Set<ShapeId> removedErrors = shapes.stream() | ||
.filter(shape -> shape.hasTrait(ErrorTrait.class)) | ||
.map(Shape::getId) | ||
.collect(Collectors.toSet()); | ||
|
||
Set<Shape> toReplace = getServicesToUpdate(model, removedOperations, removedErrors); | ||
return transformer.replaceShapes(model, toReplace); | ||
} | ||
|
||
private Set<Shape> getServicesToUpdate(Model model, Set<ShapeId> removedOperations, Set<ShapeId> removedErrors) { | ||
return model.getShapeIndex().shapes(ServiceShape.class) | ||
.flatMap(service -> Trait.flatMapStream(service, EndpointDiscoveryTrait.class)) | ||
.filter(pair -> removedOperations.contains(pair.getRight().getOperation()) | ||
|| removedErrors.contains(pair.getRight().getError())) | ||
.map(pair -> { | ||
ServiceShape.Builder builder = pair.getLeft().toBuilder(); | ||
builder.removeTrait(EndpointDiscoveryTrait.ID); | ||
return builder.build(); | ||
}) | ||
.collect(Collectors.toSet()); | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
...ain/java/software/amazon/smithy/aws/traits/endpointdiscovery/DiscoveredEndpointTrait.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright 2019 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.aws.traits.endpointdiscovery; | ||
|
||
import java.util.Collections; | ||
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.AbstractTrait; | ||
import software.amazon.smithy.model.traits.AbstractTraitBuilder; | ||
import software.amazon.smithy.utils.SmithyBuilder; | ||
import software.amazon.smithy.utils.ToSmithyBuilder; | ||
|
||
/** | ||
* Indicates that the target operation should use the SDK's endpoint discovery | ||
* logic. | ||
*/ | ||
public final class DiscoveredEndpointTrait extends AbstractTrait implements ToSmithyBuilder<DiscoveredEndpointTrait> { | ||
public static final ShapeId ID = ShapeId.from("aws.api#discoveredEndpoint"); | ||
|
||
private static final String REQUIRED = "required"; | ||
|
||
private final boolean required; | ||
|
||
private DiscoveredEndpointTrait(Builder builder) { | ||
super(ID, builder.getSourceLocation()); | ||
this.required = builder.required; | ||
} | ||
|
||
/** | ||
* @return Returns a builder used to create {@link DiscoveredEndpointTrait} | ||
*/ | ||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
/** | ||
* @return Returns whether or not the service requires endpoint discovery. | ||
*/ | ||
public boolean isRequired() { | ||
return required; | ||
} | ||
|
||
@Override | ||
protected Node createNode() { | ||
return Node.objectNode().withMember(REQUIRED, Node.from(isRequired())); | ||
} | ||
|
||
@Override | ||
public SmithyBuilder<DiscoveredEndpointTrait> toBuilder() { | ||
return builder().required(required); | ||
} | ||
|
||
/** Builder for {@link DiscoveredEndpointTrait}. */ | ||
public static final class Builder extends AbstractTraitBuilder<DiscoveredEndpointTrait, Builder> { | ||
private boolean required; | ||
|
||
private Builder() {} | ||
|
||
@Override | ||
public DiscoveredEndpointTrait build() { | ||
return new DiscoveredEndpointTrait(this); | ||
} | ||
|
||
public Builder required(boolean required) { | ||
this.required = required; | ||
return this; | ||
} | ||
} | ||
|
||
public static final class Provider extends AbstractTrait.Provider { | ||
public Provider() { | ||
super(ID); | ||
} | ||
|
||
@Override | ||
public DiscoveredEndpointTrait createTrait(ShapeId target, Node value) { | ||
ObjectNode objectNode = value.expectObjectNode(); | ||
objectNode.warnIfAdditionalProperties(Collections.singletonList(REQUIRED)); | ||
|
||
return builder() | ||
.required(objectNode.getBooleanMemberOrDefault(REQUIRED, true)) | ||
.build(); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...in/java/software/amazon/smithy/aws/traits/endpointdiscovery/EndpointDiscoveryIdTrait.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2019 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.aws.traits.endpointdiscovery; | ||
|
||
import software.amazon.smithy.model.SourceLocation; | ||
import software.amazon.smithy.model.shapes.ShapeId; | ||
import software.amazon.smithy.model.traits.BooleanTrait; | ||
|
||
/** | ||
* Indicates members of the operation input which should be use to discover | ||
* endpoints. | ||
*/ | ||
public final class EndpointDiscoveryIdTrait extends BooleanTrait { | ||
public static final ShapeId ID = ShapeId.from("aws.api#endpointDiscoveryId"); | ||
|
||
public EndpointDiscoveryIdTrait(SourceLocation sourceLocation) { | ||
super(ID, sourceLocation); | ||
} | ||
|
||
public EndpointDiscoveryIdTrait() { | ||
this(SourceLocation.NONE); | ||
} | ||
|
||
public static final class Provider extends BooleanTrait.Provider<EndpointDiscoveryIdTrait> { | ||
public Provider() { | ||
super(ID, EndpointDiscoveryIdTrait::new); | ||
} | ||
} | ||
} |
114 changes: 114 additions & 0 deletions
114
...main/java/software/amazon/smithy/aws/traits/endpointdiscovery/EndpointDiscoveryIndex.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright 2019 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.aws.traits.endpointdiscovery; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import software.amazon.smithy.model.Model; | ||
import software.amazon.smithy.model.knowledge.KnowledgeIndex; | ||
import software.amazon.smithy.model.knowledge.OperationIndex; | ||
import software.amazon.smithy.model.knowledge.TopDownIndex; | ||
import software.amazon.smithy.model.shapes.MemberShape; | ||
import software.amazon.smithy.model.shapes.OperationShape; | ||
import software.amazon.smithy.model.shapes.ServiceShape; | ||
import software.amazon.smithy.model.shapes.Shape; | ||
import software.amazon.smithy.model.shapes.ShapeId; | ||
import software.amazon.smithy.model.shapes.ShapeIndex; | ||
import software.amazon.smithy.model.shapes.StructureShape; | ||
import software.amazon.smithy.model.shapes.ToShapeId; | ||
import software.amazon.smithy.model.traits.Trait; | ||
import software.amazon.smithy.utils.Pair; | ||
|
||
public final class EndpointDiscoveryIndex implements KnowledgeIndex { | ||
private final Map<ShapeId, Map<ShapeId, EndpointDiscoveryInfo>> endpointDiscoveryInfo = new HashMap<>(); | ||
|
||
public EndpointDiscoveryIndex(Model model) { | ||
ShapeIndex index = model.getShapeIndex(); | ||
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class); | ||
OperationIndex opIndex = model.getKnowledge(OperationIndex.class); | ||
|
||
index.shapes(ServiceShape.class) | ||
.flatMap(service -> Trait.flatMapStream(service, EndpointDiscoveryTrait.class)) | ||
.forEach(servicePair -> { | ||
ServiceShape service = servicePair.getLeft(); | ||
ShapeId endpointOperationId = servicePair.getRight().getOperation(); | ||
ShapeId endpointErrorId = servicePair.getRight().getError(); | ||
|
||
Optional<OperationShape> endpointOperation = index.getShape(endpointOperationId) | ||
.flatMap(Shape::asOperationShape); | ||
Optional<StructureShape> endpointError = index.getShape(endpointErrorId) | ||
.flatMap(Shape::asStructureShape); | ||
|
||
if (endpointOperation.isPresent() && endpointError.isPresent()) { | ||
Map<ShapeId, EndpointDiscoveryInfo> serviceInfo = getOperations( | ||
service, endpointOperation.get(), endpointError.get(), topDownIndex, opIndex); | ||
if (!serviceInfo.isEmpty()) { | ||
endpointDiscoveryInfo.put(service.getId(), serviceInfo); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
private Map<ShapeId, EndpointDiscoveryInfo> getOperations( | ||
ServiceShape service, | ||
OperationShape endpointOperation, | ||
StructureShape endpointError, | ||
TopDownIndex topDownIndex, | ||
OperationIndex opIndex | ||
) { | ||
return topDownIndex.getContainedOperations(service).stream() | ||
.flatMap(operation -> Trait.flatMapStream(operation, DiscoveredEndpointTrait.class)) | ||
.map(pair -> { | ||
OperationShape operation = pair.getLeft(); | ||
List<MemberShape> discoveryIds = getDiscoveryIds(opIndex, operation); | ||
EndpointDiscoveryInfo info = new EndpointDiscoveryInfo( | ||
service, | ||
operation, | ||
endpointOperation, | ||
endpointError, | ||
discoveryIds, | ||
pair.getRight().isRequired() | ||
); | ||
return Pair.of(operation.getId(), info); | ||
}) | ||
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); | ||
} | ||
|
||
private List<MemberShape> getDiscoveryIds(OperationIndex opIndex, OperationShape operation) { | ||
List<MemberShape> discoveryIds = new ArrayList<>(); | ||
opIndex.getInput(operation).ifPresent(input -> input.getAllMembers().values().stream() | ||
.filter(member -> member.hasTrait(EndpointDiscoveryIdTrait.class)) | ||
.forEach(discoveryIds::add)); | ||
return discoveryIds; | ||
} | ||
|
||
public Optional<EndpointDiscoveryInfo> getEndpointDiscoveryInfo(ToShapeId service, ToShapeId operation) { | ||
return Optional.ofNullable(endpointDiscoveryInfo.get(service.toShapeId())) | ||
.flatMap(mappings -> Optional.ofNullable(mappings.get(operation.toShapeId()))); | ||
} | ||
|
||
public Set<ShapeId> getEndpointDiscoveryOperations(ToShapeId service) { | ||
return Optional.ofNullable(endpointDiscoveryInfo.get(service.toShapeId())) | ||
.flatMap(mappings -> Optional.of(mappings.keySet())) | ||
.orElse(new HashSet<>()); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
.../main/java/software/amazon/smithy/aws/traits/endpointdiscovery/EndpointDiscoveryInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright 2019 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.aws.traits.endpointdiscovery; | ||
|
||
import java.util.List; | ||
import software.amazon.smithy.model.shapes.MemberShape; | ||
import software.amazon.smithy.model.shapes.OperationShape; | ||
import software.amazon.smithy.model.shapes.ServiceShape; | ||
import software.amazon.smithy.model.shapes.StructureShape; | ||
|
||
public final class EndpointDiscoveryInfo { | ||
|
||
private final ServiceShape service; | ||
private final OperationShape operation; | ||
private final OperationShape discoveryOperation; | ||
private final StructureShape error; | ||
private final List<MemberShape> discoveryIds; | ||
private final boolean required; | ||
|
||
EndpointDiscoveryInfo( | ||
ServiceShape service, | ||
OperationShape operation, | ||
OperationShape discoveryOperation, | ||
StructureShape error, | ||
List<MemberShape> discoveryIds, | ||
boolean required | ||
) { | ||
this.service = service; | ||
this.operation = operation; | ||
this.discoveryOperation = discoveryOperation; | ||
this.error = error; | ||
this.discoveryIds = discoveryIds; | ||
this.required = required; | ||
} | ||
|
||
public ServiceShape getService() { | ||
return service; | ||
} | ||
|
||
public OperationShape getOperation() { | ||
return operation; | ||
} | ||
|
||
public OperationShape getDiscoveryOperation() { | ||
return discoveryOperation; | ||
} | ||
|
||
public StructureShape getError() { | ||
return error; | ||
} | ||
|
||
public List<MemberShape> getDiscoveryIds() { | ||
return discoveryIds; | ||
} | ||
|
||
public boolean isRequired() { | ||
return required; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other
ModelTransformerPlugin
implementations handle all relevant changes to make sure the resulting model is in a valid state. Could this do so viafilterTraits
on the other traits? Should it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think they are related but separate concerns. By decorating an operation with
discoveredEndpoint
you're saying that it needs to have an endpoint discovered for it to work (or at least work optimally). I don't think that changes if you project away the operation used for discovery.