Skip to content

Commit

Permalink
Run the taggable resource validator only on resources bound to the se…
Browse files Browse the repository at this point in the history
…rvice
  • Loading branch information
sugmanue committed Dec 6, 2024
1 parent 9563d77 commit 0bf70f9
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import static software.amazon.smithy.aws.traits.tagging.TaggingShapeUtils.TAG_RESOURCE_OPNAME;
import static software.amazon.smithy.aws.traits.tagging.TaggingShapeUtils.UNTAG_RESOURCE_OPNAME;

import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import software.amazon.smithy.model.FromSourceLocation;
Expand All @@ -36,15 +36,15 @@ public final class ServiceTaggingValidator extends AbstractValidator {
@Override
public List<ValidationEvent> validate(Model model) {
AwsTagIndex awsTagIndex = AwsTagIndex.of(model);
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
for (ServiceShape service : model.getServiceShapesWithTrait(TagEnabledTrait.class)) {
events.addAll(validateService(service, awsTagIndex));
}
return events;
}

private List<ValidationEvent> validateService(ServiceShape service, AwsTagIndex awsTagIndex) {
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
TagEnabledTrait trait = service.expectTrait(TagEnabledTrait.class);

Optional<ShapeId> tagResourceId = awsTagIndex.getTagResourceOperation(service.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package software.amazon.smithy.aws.traits.tagging;

import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.TopDownIndex;
Expand All @@ -30,7 +30,7 @@
public final class TagEnabledServiceValidator extends AbstractValidator {
@Override
public List<ValidationEvent> validate(Model model) {
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
TopDownIndex topDownIndex = TopDownIndex.of(model);
AwsTagIndex tagIndex = AwsTagIndex.of(model);
for (ServiceShape service : model.getServiceShapesWithTrait(TagEnabledTrait.class)) {
Expand All @@ -44,7 +44,7 @@ private List<ValidationEvent> validateService(
AwsTagIndex tagIndex,
TopDownIndex topDownIndex
) {
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
TagEnabledTrait trait = service.expectTrait(TagEnabledTrait.class);

int taggableResourceCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
package software.amazon.smithy.aws.traits.tagging;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -40,13 +40,13 @@
public final class TaggableResourceValidator extends AbstractValidator {
@Override
public List<ValidationEvent> validate(Model model) {
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
TopDownIndex topDownIndex = TopDownIndex.of(model);
AwsTagIndex tagIndex = AwsTagIndex.of(model);
for (ServiceShape service : model.getServiceShapes()) {
for (ResourceShape resource : topDownIndex.getContainedResources(service)) {
boolean resourceLikelyTaggable = false;
if (resource.hasTrait(TaggableTrait.class)) {
if (resource.hasTrait(TaggableTrait.class) && service.getResources().contains(resource.getId())) {
events.addAll(validateResource(model, resource, service, tagIndex));
resourceLikelyTaggable = true;
} else if (resource.hasTrait(ArnTrait.class) && tagIndex.serviceHasTagApis(service)) {
Expand All @@ -72,7 +72,7 @@ private List<ValidationEvent> validateResource(
ServiceShape service,
AwsTagIndex awsTagIndex
) {
List<ValidationEvent> events = new LinkedList<>();
List<ValidationEvent> events = new ArrayList<>();
// Generate danger if resource has tag property in update API.
if (awsTagIndex.isResourceTagOnUpdate(resource.getId())) {
Shape operation = resource.getUpdate().isPresent()
Expand Down Expand Up @@ -174,7 +174,7 @@ private boolean exactlyOne(
}

private Collection<Map.Entry<MemberShape, Shape>> collectMemberTargetShapes(ShapeId ioShapeId, Model model) {
Collection<Map.Entry<MemberShape, Shape>> collection = new LinkedList<>();
Collection<Map.Entry<MemberShape, Shape>> collection = new ArrayList<>();
for (MemberShape memberShape : model.expectShape(ioShapeId).members()) {
collection.add(new AbstractMap.SimpleImmutableEntry<>(
memberShape, model.expectShape(memberShape.getTarget())));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[WARNING] example.weather#Forecast: Resource is likely missing `aws.api#taggable` trait. | TaggableResource
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
$version: "2.0"

metadata suppressions = [
{
id: "UnstableTrait",
namespace: "example.weather"
}
]

namespace example.weather

use aws.api#arn
use aws.api#taggable
use aws.api#tagEnabled

@tagEnabled
service Weather {
version: "2006-03-01",
resources: [City]
operations: [GetCurrentTime, TagResource, UntagResource, ListTagsForResource]
}

@internal
service AnotherService {
version: "2006-03-01"
resources: [AnotherResouce]
}

@arn(template: "resource/{resourceId}/another")
resource AnotherResouce {
identifiers: {
cityId: CityId
}
resources: [
City
]
}

structure Tag {
key: String
value: String
}

list TagList {
member: Tag
}

list TagKeys {
member: String
}

operation TagResource {
input := {
@required
arn: String
@length(max: 128)
tags: TagList
}
output := { }
}

operation UntagResource {
input := {
@required
arn: String
@required
tagKeys: TagKeys
}
output := { }
}

operation ListTagsForResource {
input := {
@required
arn: String
}
output := {
@length(max: 128)
tags: TagList
}
}

@arn(
template: "city/{cityId}/forecast/{forecastId}"
)
resource Forecast {
identifiers: {
cityId: CityId
forecastId: ForecastId
}
}

@taggable(property: "tags")
@arn(template: "city/{CityId}")
resource City {
identifiers: { cityId: CityId }
properties: {
name: String
coordinates: CityCoordinates
}
read: GetCity
resources: [Forecast]
}

@pattern("^[A-Za-z0-9 ]+$")
string ForecastId

@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input: GetCityInput
output: GetCityOutput
errors: [NoSuchResource]
}

@input
structure GetCityInput {
// "cityId" provides the identifier for the resource and
// has to be marked as required.
@required
cityId: CityId
}

@output
structure GetCityOutput {
// "required" is used on output to indicate if the service
// will always provide a value for the member.
@required
name: String,

@required
coordinates: CityCoordinates
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float,

@required
longitude: Float,
}

// "error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

@readonly
operation GetCurrentTime {
input: GetCurrentTimeInput
output: GetCurrentTimeOutput
}

@input
structure GetCurrentTimeInput {}

@output
structure GetCurrentTimeOutput {
@required
time: Timestamp
}

0 comments on commit 0bf70f9

Please sign in to comment.