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

Simplify prelude loading #524

Merged
merged 1 commit into from
Aug 7, 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,88 +15,9 @@

package software.amazon.smithy.model.loader;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.BigDecimalShape;
import software.amazon.smithy.model.shapes.BigIntegerShape;
import software.amazon.smithy.model.shapes.BlobShape;
import software.amazon.smithy.model.shapes.BooleanShape;
import software.amazon.smithy.model.shapes.ByteShape;
import software.amazon.smithy.model.shapes.DocumentShape;
import software.amazon.smithy.model.shapes.DoubleShape;
import software.amazon.smithy.model.shapes.FloatShape;
import software.amazon.smithy.model.shapes.IntegerShape;
import software.amazon.smithy.model.shapes.LongShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.ShortShape;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.TimestampShape;
import software.amazon.smithy.model.shapes.ToShapeId;
import software.amazon.smithy.model.traits.AuthDefinitionTrait;
import software.amazon.smithy.model.traits.AuthTrait;
import software.amazon.smithy.model.traits.BoxTrait;
import software.amazon.smithy.model.traits.CorsTrait;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.EndpointTrait;
import software.amazon.smithy.model.traits.EnumTrait;
import software.amazon.smithy.model.traits.ErrorTrait;
import software.amazon.smithy.model.traits.EventHeaderTrait;
import software.amazon.smithy.model.traits.EventPayloadTrait;
import software.amazon.smithy.model.traits.ExamplesTrait;
import software.amazon.smithy.model.traits.ExternalDocumentationTrait;
import software.amazon.smithy.model.traits.HostLabelTrait;
import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait;
import software.amazon.smithy.model.traits.HttpBasicAuthTrait;
import software.amazon.smithy.model.traits.HttpBearerAuthTrait;
import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait;
import software.amazon.smithy.model.traits.HttpDigestAuthTrait;
import software.amazon.smithy.model.traits.HttpErrorTrait;
import software.amazon.smithy.model.traits.HttpHeaderTrait;
import software.amazon.smithy.model.traits.HttpLabelTrait;
import software.amazon.smithy.model.traits.HttpPayloadTrait;
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait;
import software.amazon.smithy.model.traits.HttpQueryTrait;
import software.amazon.smithy.model.traits.HttpTrait;
import software.amazon.smithy.model.traits.IdRefTrait;
import software.amazon.smithy.model.traits.IdempotencyTokenTrait;
import software.amazon.smithy.model.traits.IdempotentTrait;
import software.amazon.smithy.model.traits.JsonNameTrait;
import software.amazon.smithy.model.traits.LengthTrait;
import software.amazon.smithy.model.traits.MediaTypeTrait;
import software.amazon.smithy.model.traits.NoReplaceTrait;
import software.amazon.smithy.model.traits.OptionalAuthTrait;
import software.amazon.smithy.model.traits.PaginatedTrait;
import software.amazon.smithy.model.traits.PatternTrait;
import software.amazon.smithy.model.traits.PrivateTrait;
import software.amazon.smithy.model.traits.ProtocolDefinitionTrait;
import software.amazon.smithy.model.traits.RangeTrait;
import software.amazon.smithy.model.traits.ReadonlyTrait;
import software.amazon.smithy.model.traits.ReferencesTrait;
import software.amazon.smithy.model.traits.RequiredTrait;
import software.amazon.smithy.model.traits.RequiresLengthTrait;
import software.amazon.smithy.model.traits.ResourceIdentifierTrait;
import software.amazon.smithy.model.traits.RetryableTrait;
import software.amazon.smithy.model.traits.SensitiveTrait;
import software.amazon.smithy.model.traits.SinceTrait;
import software.amazon.smithy.model.traits.StreamingTrait;
import software.amazon.smithy.model.traits.SuppressTrait;
import software.amazon.smithy.model.traits.TagsTrait;
import software.amazon.smithy.model.traits.TimestampFormatTrait;
import software.amazon.smithy.model.traits.TitleTrait;
import software.amazon.smithy.model.traits.TraitDefinition;
import software.amazon.smithy.model.traits.TraitFactory;
import software.amazon.smithy.model.traits.UniqueItemsTrait;
import software.amazon.smithy.model.traits.UnstableTrait;
import software.amazon.smithy.model.traits.XmlAttributeTrait;
import software.amazon.smithy.model.traits.XmlFlattenedTrait;
import software.amazon.smithy.model.traits.XmlNameTrait;
import software.amazon.smithy.model.traits.XmlNamespaceTrait;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SetUtils;

/**
* Represents the prelude model available to every Smithy model.
Expand All @@ -110,110 +31,10 @@
* result in infinite recursion while loading the prelude model.
*/
public final class Prelude {

/** The Smithy prelude namespace. */
public static final String NAMESPACE = "smithy.api";

private static final List<Shape> PUBLIC_PRELUDE_SHAPES = ListUtils.of(
StringShape.builder().id(NAMESPACE + "#String").build(),
BlobShape.builder().id(NAMESPACE + "#Blob").build(),
BigIntegerShape.builder().id(NAMESPACE + "#BigInteger").build(),
BigDecimalShape.builder().id(NAMESPACE + "#BigDecimal").build(),
TimestampShape.builder().id(NAMESPACE + "#Timestamp").build(),
DocumentShape.builder().id(NAMESPACE + "#Document").build(),
BooleanShape.builder().id(NAMESPACE + "#Boolean").addTrait(new BoxTrait()).build(),
BooleanShape.builder().id(NAMESPACE + "#PrimitiveBoolean").build(),
ByteShape.builder().id(NAMESPACE + "#Byte").addTrait(new BoxTrait()).build(),
ByteShape.builder().id(NAMESPACE + "#PrimitiveByte").build(),
ShortShape.builder().id(NAMESPACE + "#Short").addTrait(new BoxTrait()).build(),
ShortShape.builder().id(NAMESPACE + "#PrimitiveShort").build(),
IntegerShape.builder().id(NAMESPACE + "#Integer").addTrait(new BoxTrait()).build(),
IntegerShape.builder().id(NAMESPACE + "#PrimitiveInteger").build(),
LongShape.builder().id(NAMESPACE + "#Long").addTrait(new BoxTrait()).build(),
LongShape.builder().id(NAMESPACE + "#PrimitiveLong").build(),
FloatShape.builder().id(NAMESPACE + "#Float").addTrait(new BoxTrait()).build(),
FloatShape.builder().id(NAMESPACE + "#PrimitiveFloat").build(),
DoubleShape.builder().id(NAMESPACE + "#Double").addTrait(new BoxTrait()).build(),
DoubleShape.builder().id(NAMESPACE + "#PrimitiveDouble").build());

/**
* This list of public prelude traits is manually maintained and must match the
* public prelude traits defined in prelude-traits.smithy. The Prelude itself
* cannot refer to the loaded model because the Prelude abstraction is queried
* while loading the prelude model.
*
* <p>A check is made each time the prelude is first loaded to ensure that the
* actual traits defined in the prelude match the traits in this list. As long
* as changes are tested before releasing, they should never get out of sync.
*/
private static final Set<ShapeId> PRELUDE_TRAITS = SetUtils.of(
AuthTrait.ID,
BoxTrait.ID,
CorsTrait.ID,
DeprecatedTrait.ID,
DocumentationTrait.ID,
EndpointTrait.ID,
EnumTrait.ID,
ErrorTrait.ID,
EventHeaderTrait.ID,
EventPayloadTrait.ID,
ExamplesTrait.ID,
ExternalDocumentationTrait.ID,
HostLabelTrait.ID,
HttpChecksumRequiredTrait.ID,
HttpErrorTrait.ID,
HttpHeaderTrait.ID,
HttpLabelTrait.ID,
HttpPayloadTrait.ID,
HttpPrefixHeadersTrait.ID,
HttpQueryTrait.ID,
HttpTrait.ID,
IdRefTrait.ID,
IdempotencyTokenTrait.ID,
IdempotentTrait.ID,
JsonNameTrait.ID,
LengthTrait.ID,
NoReplaceTrait.ID,
MediaTypeTrait.ID,
PaginatedTrait.ID,
PatternTrait.ID,
PrivateTrait.ID,
ProtocolDefinitionTrait.ID,
AuthDefinitionTrait.ID,
HttpApiKeyAuthTrait.ID,
HttpBasicAuthTrait.ID,
HttpDigestAuthTrait.ID,
HttpBearerAuthTrait.ID,
OptionalAuthTrait.ID,
RangeTrait.ID,
ReadonlyTrait.ID,
ReferencesTrait.ID,
RequiresLengthTrait.ID,
RequiredTrait.ID,
ResourceIdentifierTrait.ID,
RetryableTrait.ID,
SensitiveTrait.ID,
SinceTrait.ID,
StreamingTrait.ID,
SuppressTrait.ID,
TagsTrait.ID,
TimestampFormatTrait.ID,
TitleTrait.ID,
TraitDefinition.ID,
UniqueItemsTrait.ID,
UnstableTrait.ID,
XmlAttributeTrait.ID,
XmlFlattenedTrait.ID,
XmlNameTrait.ID,
XmlNamespaceTrait.ID);

private static final Set<ShapeId> PUBLIC_PRELUDE_SHAPE_IDS;

static {
PUBLIC_PRELUDE_SHAPE_IDS = PUBLIC_PRELUDE_SHAPES.stream()
.map(Shape::getId)
.collect(SetUtils.toUnmodifiableSet());
}

private Prelude() {}

/**
Expand All @@ -237,8 +58,9 @@ public static boolean isPreludeShape(ToShapeId id) {
* @return Returns true if the shape is a public prelude shape.
*/
public static boolean isPublicPreludeShape(ToShapeId id) {
ShapeId toId = id.toShapeId();
return PUBLIC_PRELUDE_SHAPE_IDS.contains(toId) || PRELUDE_TRAITS.contains(toId);
return getPreludeModel().getShape(id.toShapeId())
.filter(shape -> !shape.hasTrait(PrivateTrait.class))
.isPresent();
}

// Used by the ModelAssembler to load the prelude into another visitor.
Expand All @@ -251,33 +73,12 @@ private static final class PreludeHolder {
private static final Model PRELUDE = loadPrelude();

private static Model loadPrelude() {
TraitFactory traitFactory = ModelAssembler.LazyTraitFactoryHolder.INSTANCE;
ModelAssembler assembler = Model.assembler()
return Model.assembler()
.disablePrelude()
.traitFactory(traitFactory)
.addImport(Prelude.class.getResource("prelude-traits.smithy"));

for (Shape shape : PUBLIC_PRELUDE_SHAPES) {
assembler.addShape(shape);
}

Model preludeModel = assembler.assemble().unwrap();

// Sanity check to ensure that the prelude model and the tracked prelude traits are consistent.
// TODO: Can this be moved to a build step in Gradle?
for (Shape trait : preludeModel.getShapesWithTrait(TraitDefinition.class)) {
if (!PRELUDE_TRAITS.contains(trait.getId())) {
throw new IllegalStateException(
"PRELUDE_TRAITS property of prelude is inconsistent with the traits defined in the "
+ "prelude-traits.smithy file. This property MUST be kept consistent with the file. "
+ PRELUDE_TRAITS + " in PRELUDE_TRAITS vs "
+ preludeModel.getShapesWithTrait(TraitDefinition.class).stream()
.map(Shape::getId)
.collect(Collectors.toList()));
}
}

return preludeModel;
.traitFactory(ModelAssembler.LazyTraitFactoryHolder.INSTANCE)
.addImport(Prelude.class.getResource("prelude.smithy"))
.assemble()
.unwrap();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,57 @@ $version: "1.0"

namespace smithy.api

// ------ Prelude shapes

string String

blob Blob

bigInteger BigInteger

bigDecimal BigDecimal

timestamp Timestamp

document Document

@box
boolean Boolean

boolean PrimitiveBoolean

@box
byte Byte

byte PrimitiveByte

@box
short Short

short PrimitiveShort

@box
integer Integer

integer PrimitiveInteger

@box
long Long

long PrimitiveLong

@box
float Float

float PrimitiveFloat

@box
double Double

double PrimitiveDouble

// ------ Prelude traits

/// Makes a shape a trait.
@trait(selector: ":is(simpleType, list, map, set, structure, union)")
@tags(["diff.error.add", "diff.error.remove"])
Expand Down