Skip to content

Commit

Permalink
Detect when service errors are added or removed
Browse files Browse the repository at this point in the history
  • Loading branch information
mtdowling committed Sep 24, 2021
1 parent e7990b2 commit 039c5cf
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2021 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.diff.evaluators;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import software.amazon.smithy.diff.ChangedShape;
import software.amazon.smithy.diff.Differences;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.validation.ValidationEvent;

/**
* Emits a warning when an error is added to a service.
*/
public final class AddedServiceError extends AbstractDiffEvaluator {
@Override
public List<ValidationEvent> evaluate(Differences differences) {
return differences.changedShapes(ServiceShape.class)
.flatMap(change -> createErrorViolations(change).stream())
.collect(Collectors.toList());
}

private List<ValidationEvent> createErrorViolations(ChangedShape<ServiceShape> change) {
if (change.getOldShape().getErrors().equals(change.getNewShape().getErrors())) {
return Collections.emptyList();
}

List<ValidationEvent> events = new ArrayList<>();
for (ShapeId id : change.getNewShape().getErrors()) {
if (!change.getOldShape().getErrors().contains(id)) {
events.add(warning(change.getNewShape(), String.format(
"The `%s` error was added to the `%s` service, making this error common "
+ "to all operations within the service. This is backward-compatible if the "
+ "error is only encountered as a result of a change in behavior of "
+ "the client (for example, the client sends a new "
+ "parameter to an operation).",
id, change.getShapeId())));
}
}

return events;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2021 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.diff.evaluators;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import software.amazon.smithy.diff.ChangedShape;
import software.amazon.smithy.diff.Differences;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.validation.ValidationEvent;

/**
* Emits a warning when an error is removed from a service.
*/
public final class RemovedServiceError extends AbstractDiffEvaluator {
@Override
public List<ValidationEvent> evaluate(Differences differences) {
return differences.changedShapes(ServiceShape.class)
.flatMap(change -> createErrorViolations(change).stream())
.collect(Collectors.toList());
}

private List<ValidationEvent> createErrorViolations(ChangedShape<ServiceShape> change) {
if (change.getOldShape().getErrors().equals(change.getNewShape().getErrors())) {
return Collections.emptyList();
}

List<ValidationEvent> events = new ArrayList<>();
for (ShapeId id : change.getOldShape().getErrors()) {
if (!change.getNewShape().getErrors().contains(id)) {
events.add(warning(change.getNewShape(), String.format(
"The `%s` error was removed from the `%s` service. This means that it "
+ "is no longer considered an error common to all operations within the "
+ "service.",
change.getShapeId(), id)));
}
}

return events;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ software.amazon.smithy.diff.evaluators.AddedEntityBinding
software.amazon.smithy.diff.evaluators.AddedMetadata
software.amazon.smithy.diff.evaluators.AddedOperationError
software.amazon.smithy.diff.evaluators.AddedOperationInputOutput
software.amazon.smithy.diff.evaluators.AddedServiceError
software.amazon.smithy.diff.evaluators.AddedShape
software.amazon.smithy.diff.evaluators.AddedTraitDefinition
software.amazon.smithy.diff.evaluators.ChangedEnumTrait
Expand All @@ -21,6 +22,7 @@ software.amazon.smithy.diff.evaluators.RemovedMetadata
software.amazon.smithy.diff.evaluators.RemovedOperationError
software.amazon.smithy.diff.evaluators.RemovedOperationInput
software.amazon.smithy.diff.evaluators.RemovedOperationOutput
software.amazon.smithy.diff.evaluators.RemovedServiceError
software.amazon.smithy.diff.evaluators.RemovedShape
software.amazon.smithy.diff.evaluators.RemovedTraitDefinition
software.amazon.smithy.diff.evaluators.ServiceRename
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2021 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.diff.evaluators;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.util.List;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.diff.ModelDiff;
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.StructureShape;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.validation.ValidationEvent;

public class AddedServiceErrorTest {
@Test
public void detectsAddedErrors() {
Shape e1 = StructureShape.builder()
.id("foo.baz#E1")
.addTrait(new ErrorTrait("client"))
.build();
Shape e2 = StructureShape.builder()
.id("foo.baz#E2")
.addTrait(new ErrorTrait("client"))
.build();
ServiceShape service1 = ServiceShape.builder().id("foo.baz#S").version("X").build();
ServiceShape service2 = service1.toBuilder().addError(e1.getId()).addError(e2.getId()).build();
Model modelA = Model.assembler().addShapes(service1, e1, e2).assemble().unwrap();
Model modelB = Model.assembler().addShapes(service2, e1, e2).assemble().unwrap();
List<ValidationEvent> events = ModelDiff.compare(modelA, modelB);

assertThat(TestHelper.findEvents(events, "AddedServiceError").size(), equalTo(2));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ public void detectsRemovedErrors() {
.id("foo.baz#E2")
.addTrait(new ErrorTrait("client"))
.build();
OperationShape operation1 = OperationShape.builder().id("foo.baz#Operation").build();
Shape operation2 = operation1.toBuilder().addError(e1.getId()).addError(e2.getId()).build();
Model modelA = Model.assembler().addShapes(operation2, e1, e2).assemble().unwrap();
Model modelB = Model.assembler().addShapes(operation1, e1, e2).assemble().unwrap();
OperationShape operation1 = OperationShape.builder()
.id("foo.baz#Operation")
.addError(e1)
.addError(e2)
.build();
Shape operation2 = operation1.toBuilder().clearErrors().build();
Model modelA = Model.assembler().addShapes(operation1, e1, e2).assemble().unwrap();
Model modelB = Model.assembler().addShapes(operation2, e1, e2).assemble().unwrap();
List<ValidationEvent> events = ModelDiff.compare(modelA, modelB);

// Emits an event for each removal.
assertThat(TestHelper.findEvents(events, "RemovedOperationError").size(), equalTo(2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2021 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.diff.evaluators;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.util.List;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.diff.ModelDiff;
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.StructureShape;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.validation.ValidationEvent;

public class RemovedServiceErrorTest {
@Test
public void detectsRemovedErrors() {
Shape e1 = StructureShape.builder()
.id("foo.baz#E1")
.addTrait(new ErrorTrait("client"))
.build();
Shape e2 = StructureShape.builder()
.id("foo.baz#E2")
.addTrait(new ErrorTrait("client"))
.build();
ServiceShape service1 = ServiceShape.builder()
.id("foo.baz#S")
.version("X")
.addError(e1)
.addError(e2)
.build();
ServiceShape service2 = service1.toBuilder().clearErrors().build();
Model modelA = Model.assembler().addShapes(service1, e1, e2).assemble().unwrap();
Model modelB = Model.assembler().addShapes(service2, e1, e2).assemble().unwrap();
List<ValidationEvent> events = ModelDiff.compare(modelA, modelB);

// Emits one even for both removals.
assertThat(TestHelper.findEvents(events, "RemovedServiceError").size(), equalTo(2));
}
}

0 comments on commit 039c5cf

Please sign in to comment.