Unverified Commit 4c779651 authored by Luca Palmieri's avatar Luca Palmieri Committed by GitHub
Browse files

Implement @range on long and byte shapes (#2036)

* Group all override-related test cases together.

* Add @range examples for both byte and long in constraints.smithy.

* Implement @range for long and byte shapes.

* Update CHANGELOG

* Fix unit tests.

* Address comments

* Dry up further.
parent 3a9a42bb
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -242,15 +242,17 @@ message = """

* The `length` trait on `string` shapes.
* The `length` trait on `map` shapes.
* The `range` trait on `integer` shapes.
* The `range` trait on `byte` shapes.
* The `range` trait on `short` shapes.
* The `range` trait on `integer` shapes.
* The `range` trait on `long` 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", "smithy-rs#2034"]
references = ["smithy-rs#1199", "smithy-rs#1342", "smithy-rs#1401", "smithy-rs#2005", "smithy-rs#1998", "smithy-rs#2034", "smithy-rs#2036"]
meta = { "breaking" = true, "tada" = true, "bug" = false, "target" = "server" }
author = "david-perez"

+131 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ operation ConstrainedShapesOperation {
}

@http(
    uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{rangeShortLabel}/{lengthStringLabel}/{enumStringLabel}",
    uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{rangeShortLabel}/{rangeLongLabel}/{rangeByteLabel}/{lengthStringLabel}/{enumStringLabel}",
    method: "POST"
)
operation ConstrainedHttpBoundShapesOperation {
@@ -186,6 +186,14 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpLabel
    rangeShortLabel: RangeShort,

    @required
    @httpLabel
    rangeLongLabel: RangeLong,

    @required
    @httpLabel
    rangeByteLabel: RangeByte,

    @required
    @httpLabel
    enumStringLabel: EnumString,
@@ -203,6 +211,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpHeader("X-Range-Short")
    rangeShortHeader: RangeShort,

    @httpHeader("X-Range-Long")
    rangeLongHeader: RangeLong,

    @httpHeader("X-Range-Byte")
    rangeByteHeader: RangeByte,

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

@@ -220,6 +234,10 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    // rangeIntegerSetHeader: SetOfRangeInteger,
    // @httpHeader("X-Range-Short-Set")
    // rangeShortSetHeader: SetOfShortInteger,
    // @httpHeader("X-Range-Long-Set")
    // rangeLongSetHeader: SetOfRangeLong,
    // @httpHeader("X-Range-Byte-Set")
    // rangeByteSetHeader: SetOfByteInteger,

    @httpHeader("X-Range-Integer-List")
    rangeIntegerListHeader: ListOfRangeInteger,
@@ -227,6 +245,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpHeader("X-Range-Short-List")
    rangeShortListHeader: ListOfRangeShort,

    @httpHeader("X-Range-Long-List")
    rangeLongListHeader: ListOfRangeLong,

    @httpHeader("X-Range-Byte-List")
    rangeByteListHeader: ListOfRangeByte,

    // TODO(https://github.com/awslabs/smithy-rs/issues/1431)
    // @httpHeader("X-Enum")
    //enumStringHeader: EnumString,
@@ -243,6 +267,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpQuery("rangeShort")
    rangeShortQuery: RangeShort,

    @httpQuery("rangeLong")
    rangeLongQuery: RangeLong,

    @httpQuery("rangeByte")
    rangeByteQuery: RangeByte,

    @httpQuery("enumString")
    enumStringQuery: EnumString,

@@ -260,12 +290,22 @@ structure ConstrainedHttpBoundShapesOperationInputOutput {
    @httpQuery("rangeShortList")
    rangeShortListQuery: ListOfRangeShort,

    @httpQuery("rangeLongList")
    rangeLongListQuery: ListOfRangeLong,

    @httpQuery("rangeByteList")
    rangeByteListQuery: ListOfRangeByte,

    // 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("rangeLongSet")
    // rangeLongSetQuery: SetOfRangeLong,
    // @httpQuery("rangeByteSet")
    // rangeByteSetQuery: SetOfRangeByte,

    @httpQuery("enumStringList")
    enumStringListQuery: ListOfEnumString,
@@ -392,6 +432,16 @@ structure ConA {
    maxRangeShort: MaxRangeShort,
    fixedValueShort: FixedValueShort,

    rangeLong: RangeLong,
    minRangeLong: MinRangeLong,
    maxRangeLong: MaxRangeLong,
    fixedValueLong: FixedValueLong,

    rangeByte: RangeByte,
    minRangeByte: MinRangeByte,
    maxRangeByte: MaxRangeByte,
    fixedValueByte: FixedValueByte,

    conBList: ConBList,
    conBList2: ConBList2,

@@ -424,6 +474,18 @@ structure ConA {
    // setOfRangeShort: SetOfRangeShort,
    mapOfRangeShort: MapOfRangeShort,

    listOfRangeLong: ListOfRangeLong,
    // 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.
    // setOfRangeLong: SetOfRangeLong,
    mapOfRangeLong: MapOfRangeLong,

    listOfRangeByte: ListOfRangeByte,
    // 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.
    // setOfRangeByte: SetOfRangeByte,
    mapOfRangeByte: MapOfRangeByte,

    nonStreamingBlob: NonStreamingBlob

    patternString: PatternString,
@@ -456,6 +518,16 @@ map MapOfRangeShort {
    value: RangeShort,
}

map MapOfRangeLong {
    key: String,
    value: RangeLong,
}

map MapOfRangeByte {
    key: String,
    value: RangeByte,
}

map MapOfEnumString {
    key: EnumString,
    value: EnumString,
@@ -503,6 +575,20 @@ map MapOfSetOfLengthString {
//     value: SetOfRangeShort,
// }

// 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 MapOfSetOfRangeLong {
//     key: String,
//     value: SetOfRangeLong,
// }

// 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 MapOfSetOfRangeByte {
//     key: String,
//     value: SetOfRangeByte,
// }

@length(min: 2, max: 8)
list LengthListOfLengthString {
    member: LengthString
@@ -555,6 +641,30 @@ short MaxRangeShort
@range(min: 10, max: 10)
short FixedValueShort

@range(min: -0, max: 10)
long RangeLong

@range(min: -10)
long MinRangeLong

@range(max: 11)
long MaxRangeLong

@range(min: 10, max: 10)
long FixedValueLong

@range(min: -0, max: 10)
byte RangeByte

@range(min: -10)
byte MinRangeByte

@range(max: 11)
byte MaxRangeByte

@range(min: 10, max: 10)
byte FixedValueByte

/// A union with constrained members.
union ConstrainedUnion {
    enumString: EnumString,
@@ -620,6 +730,26 @@ list ListOfRangeShort {
    member: RangeShort
}

// 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 SetOfRangeLong {
//     member: RangeLong
// }

list ListOfRangeLong {
    member: RangeLong
}

// 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 SetOfRangeByte {
//     member: RangeByte
// }

list ListOfRangeByte {
    member: RangeByte
}

list ListOfEnumString {
    member: EnumString
}
+3 −1
Original line number Diff line number Diff line
@@ -8,8 +8,10 @@ package software.amazon.smithy.rust.codegen.server.smithy
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.knowledge.NullableIndex
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.LongShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.ServiceShape
@@ -98,7 +100,7 @@ class ConstrainedShapeSymbolProvider(
                }
            }

            is StringShape, is IntegerShape, is ShortShape -> {
            is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape -> {
                if (shape.isDirectlyConstrained(base)) {
                    val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase())
                    symbolBuilder(shape, rustType).locatedIn(ModelsModule).build()
+3 −1
Original line number Diff line number Diff line
@@ -7,8 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.LongShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.Shape
@@ -115,7 +117,7 @@ class ConstraintViolationSymbolProvider(
                    .build()
            }

            is StringShape, is IntegerShape, is ShortShape -> {
            is StringShape, is IntegerShape, is ShortShape, is LongShape, is ByteShape -> {
                val module = shape.shapeModule()
                val rustType = RustType.Opaque(constraintViolationName, module.fullyQualifiedPath())
                Symbol.builder()
+4 −2
Original line number Diff line number Diff line
@@ -8,8 +8,10 @@ package software.amazon.smithy.rust.codegen.server.smithy
import software.amazon.smithy.codegen.core.SymbolProvider
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.neighbor.Walker
import software.amazon.smithy.model.shapes.ByteShape
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.IntegerShape
import software.amazon.smithy.model.shapes.LongShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.Shape
@@ -73,7 +75,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, is ShortShape -> this.hasTrait<RangeTrait>()
    is IntegerShape, is ShortShape, is LongShape, is ByteShape -> this.hasTrait<RangeTrait>()
    else -> false
}

@@ -99,7 +101,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, is ShortShape -> publicConstrainedTypes && this.hasTrait<RangeTrait>()
    is IntegerShape, is ShortShape, is LongShape, is ByteShape -> publicConstrainedTypes && this.hasTrait<RangeTrait>()
    is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes)
    else -> false
}
Loading