in smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/TargetValidator.java [73:138]
private Optional<ValidationEvent> validateTarget(Model model, Shape shape, Shape target, Relationship rel) {
RelationshipType relType = rel.getRelationshipType();
if (relType.getDirection() == RelationshipDirection.DIRECTED && target.hasTrait(TraitDefinition.class)) {
return Optional.of(error(shape, format(
"Found a %s reference to trait definition `%s`. Trait definitions cannot be targeted by "
+ "members or referenced by shapes in any other context other than applying them as "
+ "traits.", relType, rel.getNeighborShapeId())));
}
switch (relType) {
case MEMBER_TARGET:
// Members cannot target other members, service, operation, or resource shapes.
if (INVALID_MEMBER_TARGETS.contains(target.getType())) {
return Optional.of(error(shape, format(
"Members cannot target %s shapes, but found %s", target.getType(), target)));
}
case MAP_KEY:
return target.asMemberShape().flatMap(m -> validateMapKey(shape, m.getTarget(), model));
case RESOURCE:
if (target.getType() != ShapeType.RESOURCE) {
return Optional.of(badType(shape, target, relType, ShapeType.RESOURCE));
}
return Optional.empty();
case OPERATION:
if (target.getType() != ShapeType.OPERATION) {
return Optional.of(badType(shape, target, relType, ShapeType.OPERATION));
}
return Optional.empty();
case INPUT:
case OUTPUT:
// Input/output must target structures and cannot have the error trait.
if (target.getType() != ShapeType.STRUCTURE) {
return Optional.of(badType(shape, target, relType, ShapeType.STRUCTURE));
} else if (target.findTrait("error").isPresent()) {
return Optional.of(inputOutputWithErrorTrait(shape, target, rel.getRelationshipType()));
} else {
return Optional.empty();
}
case ERROR:
// Errors must target a structure with the error trait.
if (target.getType() != ShapeType.STRUCTURE) {
return Optional.of(badType(shape, target, relType, ShapeType.STRUCTURE));
} else if (!target.findTrait("error").isPresent()) {
return Optional.of(errorNoTrait(shape, target.getId()));
} else {
return Optional.empty();
}
case IDENTIFIER:
return validateIdentifier(shape, target);
case CREATE:
case READ:
case UPDATE:
case DELETE:
case LIST:
if (target.getType() != ShapeType.OPERATION) {
return Optional.of(error(shape, format(
"Resource %s lifecycle operation must target an operation, but found %s",
relType.toString().toLowerCase(Locale.US), target)));
} else {
return Optional.empty();
}
default:
return Optional.empty();
}
}