Unverified Commit 0adad6a7 authored by Luca Palmieri's avatar Luca Palmieri Committed by GitHub
Browse files

Implement @range on short (#2034)

* Extract a ConstrainedNumberGenerator from ConstrainedIntegerGenerator

* Update tests' expectations to assume that we support @range for short

* Add ShortShape to all the key places to get @range support

* Generalize generator tests.

* Add tests for the @range trait on short to our constraints.smithy model.

* Update changelog

* Fix type in constraints.smithy
parent 9a4c1f30
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -243,13 +243,14 @@ message = """
* The `length` trait on `string` shapes.
* The `length` trait on `map` shapes.
* The `range` trait on `integer` shapes.
* The `range` trait on `short` shapes.
* The `pattern` trait on `string` shapes.

Upon receiving a request that violates the modeled constraints, the server SDK will reject it with a message indicating why.

Unsupported (constraint trait, target shape) combinations will now fail at code generation time, whereas previously they were just ignored. This is a breaking change to raise awareness in service owners of their server SDKs behaving differently than what was modeled. To continue generating a server SDK with unsupported constraint traits, set `codegenConfig.ignoreUnsupportedConstraints` to `true` in your `smithy-build.json`.
"""
references = ["smithy-rs#1199", "smithy-rs#1342", "smithy-rs#1401", "smithy-rs#2005", "smithy-rs#1998"]
references = ["smithy-rs#1199", "smithy-rs#1342", "smithy-rs#1401", "smithy-rs#2005", "smithy-rs#1998", "smithy-rs#2034"]
meta = { "breaking" = true, "tada" = true, "bug" = false, "target" = "server" }
author = "david-perez"

+70 −2
Original line number Diff line number Diff line
@@ -49,7 +49,10 @@ operation ConstrainedShapesOperation {
    errors: [ValidationException]
}

@http(uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{lengthStringLabel}/{enumStringLabel}", method: "POST")
@http(
    uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{rangeShortLabel}/{lengthStringLabel}/{enumStringLabel}",
    method: "POST"
)
operation ConstrainedHttpBoundShapesOperation {
    input: ConstrainedHttpBoundShapesOperationInputOutput,
    output: ConstrainedHttpBoundShapesOperationInputOutput,
@@ -179,6 +182,10 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpLabel
    rangeIntegerLabel: RangeInteger,

    @required
    @httpLabel
    rangeShortLabel: RangeShort,

    @required
    @httpLabel
    enumStringLabel: EnumString,
@@ -193,6 +200,9 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpHeader("X-Range-Integer")
    rangeIntegerHeader: RangeInteger,

    @httpHeader("X-Range-Short")
    rangeShortHeader: RangeShort,

    // @httpHeader("X-Length-MediaType")
    // lengthStringHeaderWithMediaType: MediaTypeLengthString,

@@ -208,10 +218,15 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    //  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
    // @httpHeader("X-Range-Integer-Set")
    // rangeIntegerSetHeader: SetOfRangeInteger,
    // @httpHeader("X-Range-Short-Set")
    // rangeShortSetHeader: SetOfShortInteger,

    @httpHeader("X-Range-Integer-List")
    rangeIntegerListHeader: ListOfRangeInteger,

    @httpHeader("X-Range-Short-List")
    rangeShortListHeader: ListOfRangeShort,

    // TODO(https://github.com/awslabs/smithy-rs/issues/1431)
    // @httpHeader("X-Enum")
    //enumStringHeader: EnumString,
@@ -225,6 +240,9 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpQuery("rangeInteger")
    rangeIntegerQuery: RangeInteger,

    @httpQuery("rangeShort")
    rangeShortQuery: RangeShort,

    @httpQuery("enumString")
    enumStringQuery: EnumString,

@@ -239,10 +257,15 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpQuery("rangeIntegerList")
    rangeIntegerListQuery: ListOfRangeInteger,

    @httpQuery("rangeShortList")
    rangeShortListQuery: ListOfRangeShort,

    // TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
    //  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
    // @httpQuery("rangeIntegerSet")
    // rangeIntegerSetQuery: SetOfRangeInteger,
    // @httpQuery("rangeShortSet")
    // rangeShortSetQuery: SetOfRangeShort,

    @httpQuery("enumStringList")
    enumStringListQuery: ListOfEnumString,
@@ -364,6 +387,11 @@ structure ConA {
    maxRangeInteger: MaxRangeInteger,
    fixedValueInteger: FixedValueInteger,

    rangeShort: RangeShort,
    minRangeShort: MinRangeShort,
    maxRangeShort: MaxRangeShort,
    fixedValueShort: FixedValueShort,

    conBList: ConBList,
    conBList2: ConBList2,

@@ -390,6 +418,12 @@ structure ConA {
    // setOfRangeInteger: SetOfRangeInteger,
    mapOfRangeInteger: MapOfRangeInteger,

    listOfRangeShort: ListOfRangeShort,
    // TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
    //  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
    // setOfRangeShort: SetOfRangeShort,
    mapOfRangeShort: MapOfRangeShort,

    nonStreamingBlob: NonStreamingBlob

    patternString: PatternString,
@@ -417,6 +451,11 @@ map MapOfRangeInteger {
    value: RangeInteger,
}

map MapOfRangeShort {
    key: String,
    value: RangeShort,
}

map MapOfEnumString {
    key: EnumString,
    value: EnumString,
@@ -453,10 +492,17 @@ map MapOfSetOfLengthString {
// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
//  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
// map MapOfSetOfRangeInteger {
//     key: LengthString,
//     key: String,
//     value: SetOfRangeInteger,
// }

// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
//  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
// map MapOfSetOfRangeShort {
//     key: String,
//     value: SetOfRangeShort,
// }

@length(min: 2, max: 8)
list LengthListOfLengthString {
    member: LengthString
@@ -497,6 +543,18 @@ integer MaxRangeInteger
@range(min: 69, max: 69)
integer FixedValueInteger

@range(min: -0, max: 10)
short RangeShort

@range(min: -10)
short MinRangeShort

@range(max: 11)
short MaxRangeShort

@range(min: 10, max: 10)
short FixedValueShort

/// A union with constrained members.
union ConstrainedUnion {
    enumString: EnumString,
@@ -552,6 +610,16 @@ list ListOfRangeInteger {
    member: RangeInteger
}

// TODO(https://github.com/awslabs/smithy-rs/issues/1401): a `set` shape is
//  just a `list` shape with `uniqueItems`, which hasn't been implemented yet.
// set SetOfRangeShort {
//     member: RangeShort
// }

list ListOfRangeShort {
    member: RangeShort
}

list ListOfEnumString {
    member: EnumString
}
+4 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShortShape
import software.amazon.smithy.model.shapes.StringShape
import software.amazon.smithy.model.traits.LengthTrait
import software.amazon.smithy.rust.codegen.core.rustlang.RustType
@@ -96,7 +97,8 @@ class ConstrainedShapeSymbolProvider(
                    symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build()
                }
            }
            is StringShape, is IntegerShape -> {

            is StringShape, is IntegerShape, is ShortShape -> {
                if (shape.isDirectlyConstrained(base)) {
                    val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
                    symbolBuilder(shape, rustType).locatedIn(ModelsModule).build()
@@ -104,6 +106,7 @@ class ConstrainedShapeSymbolProvider(
                    base.toSymbol(shape)
                }
            }

            else -> base.toSymbol(shape)
        }
    }
+3 −1
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShortShape
import software.amazon.smithy.model.shapes.StringShape
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.model.shapes.UnionShape
@@ -113,7 +114,8 @@ class ConstraintViolationSymbolProvider(
                    .locatedIn(module)
                    .build()
            }
            is StringShape, is IntegerShape -> {

            is StringShape, is IntegerShape, is ShortShape -> {
                val module = shape.shapeModule()
                val rustType = RustType.Opaque(constraintViolationName, module.fullyQualifiedPath())
                Symbol.builder()
+3 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShortShape
import software.amazon.smithy.model.shapes.SimpleShape
import software.amazon.smithy.model.shapes.StringShape
import software.amazon.smithy.model.shapes.StructureShape
@@ -72,7 +73,7 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when

    is MapShape -> this.hasTrait<LengthTrait>()
    is StringShape -> this.hasTrait<EnumTrait>() || supportedStringConstraintTraits.any { this.hasTrait(it) }
    is IntegerShape -> this.hasTrait<RangeTrait>()
    is IntegerShape, is ShortShape -> this.hasTrait<RangeTrait>()
    else -> false
}

@@ -98,7 +99,7 @@ fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: Sym
fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTypes: Boolean): Boolean = when (this) {
    is MapShape -> publicConstrainedTypes && this.hasTrait<LengthTrait>()
    is StringShape -> !this.hasTrait<EnumTrait>() && (publicConstrainedTypes && supportedStringConstraintTraits.any(this::hasTrait))
    is IntegerShape -> publicConstrainedTypes && this.hasTrait<RangeTrait>()
    is IntegerShape, is ShortShape -> publicConstrainedTypes && this.hasTrait<RangeTrait>()
    is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes)
    else -> false
}
Loading