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

Add tags and deprecated to waiters #652

Merged
merged 1 commit into from
Dec 3, 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
10 changes: 10 additions & 0 deletions docs/source/1.0/spec/waiters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,16 @@ has entered into a desired state.
- The maximum amount of time in seconds to delay between each retry.
This value defaults to ``120`` if not specified (2 minutes). If
specified, this value MUST be greater than or equal to 1.
* - ``deprecated``
- ``boolean``
- Indicates if the waiter is considered deprecated. A waiter SHOULD
be marked as deprecated if it has been replaced by another waiter or
if it is no longer needed (for example, if a resource changes from
eventually consistent to strongly consistent).
* - ``tags``
- ``[string]``
- A list of tags associated with the waiter that allow waiters to be
categorized and grouped.


.. _waiter-acceptor:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,39 @@
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyBuilder;
import software.amazon.smithy.utils.Tagged;
import software.amazon.smithy.utils.ToSmithyBuilder;

/**
* Defines an individual operation waiter.
*/
public final class Waiter implements ToNode, ToSmithyBuilder<Waiter> {
public final class Waiter implements Tagged, ToNode, ToSmithyBuilder<Waiter> {

private static final String DOCUMENTATION = "documentation";
private static final String ACCEPTORS = "acceptors";
private static final String MIN_DELAY = "minDelay";
private static final String MAX_DELAY = "maxDelay";
private static final String DEPRECATED = "deprecated";
private static final String TAGS = "tags";
private static final int DEFAULT_MIN_DELAY = 2;
private static final int DEFAULT_MAX_DELAY = 120;
private static final Set<String> KEYS = SetUtils.of(DOCUMENTATION, ACCEPTORS, MIN_DELAY, MAX_DELAY);
private static final Set<String> KEYS = SetUtils.of(
DOCUMENTATION, ACCEPTORS, MIN_DELAY, MAX_DELAY, TAGS, DEPRECATED);

private final String documentation;
private final List<Acceptor> acceptors;
private final int minDelay;
private final int maxDelay;
private final boolean deprecated;
private final List<String> tags;

private Waiter(Builder builder) {
this.documentation = builder.documentation;
this.acceptors = ListUtils.copyOf(builder.acceptors);
this.minDelay = builder.minDelay;
this.maxDelay = builder.maxDelay;
this.deprecated = builder.deprecated;
this.tags = ListUtils.copyOf(builder.tags);
}

public static Builder builder() {
Expand All @@ -67,7 +75,9 @@ public SmithyBuilder<Waiter> toBuilder() {
.documentation(getDocumentation().orElse(null))
.acceptors(getAcceptors())
.minDelay(getMinDelay())
.maxDelay(getMaxDelay());
.maxDelay(getMaxDelay())
.tags(tags)
.deprecated(deprecated);
}

/**
Expand All @@ -81,13 +91,17 @@ public static Waiter fromNode(Node node) {
ObjectNode value = node.expectObjectNode().warnIfAdditionalProperties(KEYS);
Builder builder = builder();
value.getStringMember(DOCUMENTATION).map(StringNode::getValue).ifPresent(builder::documentation);

for (Node entry : value.expectArrayMember(ACCEPTORS).getElements()) {
builder.addAcceptor(Acceptor.fromNode(entry));
}

value.getNumberMember(MIN_DELAY).map(NumberNode::getValue).map(Number::intValue).ifPresent(builder::minDelay);
value.getNumberMember(MAX_DELAY).map(NumberNode::getValue).map(Number::intValue).ifPresent(builder::maxDelay);

builder.deprecated(value.getBooleanMemberOrDefault(DEPRECATED));
value.getMember(TAGS).ifPresent(tags -> builder.tags(Node.loadArrayOfString(TAGS, tags)));

return builder.build();
}

Expand Down Expand Up @@ -129,6 +143,20 @@ public int getMaxDelay() {
return maxDelay;
}

@Override
public List<String> getTags() {
return tags;
}

/**
* Checks if the waiter is deprecated.
*
* @return Returns true if the waiter is deprecated.
*/
public boolean isDeprecated() {
return deprecated;
}

@Override
public Node toNode() {
ObjectNode.Builder builder = Node.objectNodeBuilder()
Expand All @@ -143,6 +171,14 @@ public Node toNode() {
builder.withMember(MAX_DELAY, maxDelay);
}

if (isDeprecated()) {
builder.withMember(DEPRECATED, true);
}

if (!tags.isEmpty()) {
builder.withMember(TAGS, Node.fromStrings(tags));
}

return builder.build();
}

Expand All @@ -158,12 +194,14 @@ public boolean equals(Object o) {
return minDelay == waiter.minDelay
&& maxDelay == waiter.maxDelay
&& Objects.equals(documentation, waiter.documentation)
&& acceptors.equals(waiter.acceptors);
&& acceptors.equals(waiter.acceptors)
&& tags.equals(waiter.tags)
&& deprecated == waiter.deprecated;
}

@Override
public int hashCode() {
return Objects.hash(documentation, acceptors, minDelay, maxDelay);
return Objects.hash(documentation, acceptors, minDelay, maxDelay, deprecated, tags);
}

public static final class Builder implements SmithyBuilder<Waiter> {
Expand All @@ -172,6 +210,8 @@ public static final class Builder implements SmithyBuilder<Waiter> {
private final List<Acceptor> acceptors = new ArrayList<>();
private int minDelay = DEFAULT_MIN_DELAY;
private int maxDelay = DEFAULT_MAX_DELAY;
private boolean deprecated = false;
private final List<String> tags = new ArrayList<>();

private Builder() {}

Expand Down Expand Up @@ -210,5 +250,26 @@ public Builder maxDelay(int maxDelay) {
this.maxDelay = maxDelay;
return this;
}

public Builder clearTags() {
this.tags.clear();
return this;
}

public Builder tags(List<String> tags) {
clearTags();
tags.forEach(this::addTag);
return this;
}

public Builder addTag(String tag) {
this.tags.add(Objects.requireNonNull(tag));
return this;
}

public Builder deprecated(boolean deprecated) {
this.deprecated = deprecated;
return this;
}
}
}
19 changes: 19 additions & 0 deletions smithy-waiters/src/main/resources/META-INF/smithy/waiters.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ structure Waiter {
/// This value defaults to 120 if not specified (or, 2 minutes). If
/// specified, this value MUST be greater than or equal to 1.
maxDelay: WaiterDelay,

/// Indicates if the waiter is considered deprecated. A waiter SHOULD
/// be marked as deprecated if it has been replaced by another waiter or
/// if it is no longer needed (for example, if a resource changes from
/// eventually consistent to strongly consistent).
deprecated: PrimitiveBoolean,

/// A list of tags associated with the waiter that allow waiters to be
/// categorized and grouped.
tags: NonEmptyStringList,
}

@box
Expand Down Expand Up @@ -150,3 +160,12 @@ structure PathMatcher {
])
@private
string PathComparator

@private
list NonEmptyStringList {
member: NonEmptyString,
}

@private
@length(min: 1)
string NonEmptyString
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package software.amazon.smithy.waiters;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;

import java.util.Optional;
import org.junit.jupiter.api.Test;
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.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;

public class WaiterTest {
@Test
Expand Down Expand Up @@ -53,4 +58,26 @@ public void includesMinDelayAndMaxDelayInNodeIfNotDefaults() {
assertThat(waiter.toBuilder().build(), equalTo(waiter));
assertThat(Waiter.fromNode(waiter.toNode()), equalTo(waiter));
}

@Test
public void loadsAndPersistsWaiters() {
Model model = Model.assembler()
.discoverModels()
.addImport(getClass().getResource("errorfiles/valid-waiters.smithy"))
.assemble()
.unwrap();
Shape shape = model.expectShape(ShapeId.from("smithy.example#A"));
WaitableTrait trait = shape.expectTrait(WaitableTrait.class);

// Test that the individual waiter was loaded correctly.
assertThat(trait.getWaiters(), hasKey("F"));

Waiter waiter = trait.getWaiters().get("F");
assertThat(waiter.isDeprecated(), is(true));
assertThat(waiter.getTags(), contains("A", "B"));

// Test that the individual waiter is persisted correctly.
assertThat(Waiter.fromNode(waiter.toNode()), equalTo(waiter));
assertThat(waiter.toBuilder().build(), equalTo(waiter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ use smithy.waiters#waitable
]
},
F: {
"deprecated": true,
"tags": ["A", "B"],
"acceptors": [
{
"state": "success",
Expand Down