Detect constraints on event stream errors (#2069)

Constrained shapes in the closure of an event stream are not supported
[0]. However, the `ValidateUnsupportedConstraints` validator was not
detecting constrained shapes in event stream errors because the
`EventStreamNormalizer` model transformer pulls them out of the
`@streaming` union shape.

This commit makes it so that the validator will detect such usages, by
leveraging the `SyntheticEventStreamUnionTrait` trait that gets attached
in the model transformer.

[0]: https://github.com/awslabs/smithy/issues/1388

Co-authored-by: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com>
This commit is contained in:
david-perez 2022-12-09 12:53:03 +01:00 committed by GitHub
parent 381467d2ef
commit f54b36b7f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 13 deletions

View File

@ -19,13 +19,13 @@ import software.amazon.smithy.model.shapes.SetShape
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.UnionShape
import software.amazon.smithy.model.traits.LengthTrait
import software.amazon.smithy.model.traits.RangeTrait
import software.amazon.smithy.model.traits.RequiredTrait
import software.amazon.smithy.model.traits.StreamingTrait
import software.amazon.smithy.model.traits.Trait
import software.amazon.smithy.model.traits.UniqueItemsTrait
import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticEventStreamUnionTrait
import software.amazon.smithy.rust.codegen.core.util.expectTrait
import software.amazon.smithy.rust.codegen.core.util.getTrait
import software.amazon.smithy.rust.codegen.core.util.hasTrait
@ -213,15 +213,24 @@ fun validateUnsupportedConstraints(
// 3. Constraint traits in event streams are used. Their semantics are unclear.
// TODO(https://github.com/awslabs/smithy/issues/1388)
val unsupportedConstraintOnShapeReachableViaAnEventStreamSet = walker
val eventStreamShapes = walker
.walkShapes(service)
.asSequence()
.filterIsInstance<UnionShape>()
.filter { it.hasTrait<StreamingTrait>() }
.filter { it.hasTrait<SyntheticEventStreamUnionTrait>() }
val unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet = eventStreamShapes
.flatMap { walker.walkShapes(it) }
.filterMapShapesToTraits(allConstraintTraits)
.map { (shape, trait) -> UnsupportedConstraintOnShapeReachableViaAnEventStream(shape, trait) }
.toSet()
val eventStreamErrors = eventStreamShapes.map { it.expectTrait<SyntheticEventStreamUnionTrait>() }.map { it.errorMembers }
val unsupportedConstraintErrorShapeReachableViaAnEventStreamSet = eventStreamErrors
.flatMap { it }
.flatMap { walker.walkShapes(it) }
.filterMapShapesToTraits(allConstraintTraits)
.map { (shape, trait) -> UnsupportedConstraintOnShapeReachableViaAnEventStream(shape, trait) }
.toSet()
val unsupportedConstraintShapeReachableViaAnEventStreamSet =
unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet + unsupportedConstraintErrorShapeReachableViaAnEventStreamSet
// 4. Length trait on blob shapes is used. It has not been implemented yet.
// TODO(https://github.com/awslabs/smithy-rs/issues/1401)
@ -261,7 +270,7 @@ fun validateUnsupportedConstraints(
val messages =
unsupportedConstraintOnMemberShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedConstraintOnShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedConstraintShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedLengthTraitOnBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } +
unsupportedUniqueItemsTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) }

View File

@ -5,6 +5,7 @@
package software.amazon.smithy.rust.codegen.server.smithy
import io.kotest.inspectors.forOne
import io.kotest.inspectors.forSome
import io.kotest.inspectors.shouldForAll
import io.kotest.matchers.collections.shouldHaveAtLeastSize
@ -14,6 +15,7 @@ import io.kotest.matchers.string.shouldContain
import org.junit.jupiter.api.Test
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.util.lookup
import java.util.logging.Level
@ -130,24 +132,39 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest {
@streaming
union EventStream {
message: Message
message: Message,
error: Error
}
structure Message {
lengthString: LengthString
}
structure Error {
@required
message: String
}
@length(min: 1)
string LengthString
""".asSmithyModel()
val validationResult = validateModel(model)
val validationResult = validateModel(EventStreamNormalizer.transform(model))
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain
"""
The string shape `test#LengthString` has the constraint trait `smithy.api#length` attached.
This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are.
""".trimIndent().replace("\n", " ")
validationResult.messages shouldHaveSize 2
validationResult.messages.forOne {
it.message shouldContain
"""
The string shape `test#LengthString` has the constraint trait `smithy.api#length` attached.
This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are.
""".trimIndent().replace("\n", " ")
}
validationResult.messages.forOne {
it.message shouldContain
"""
The member shape `test#Error${"$"}message` has the constraint trait `smithy.api#required` attached.
This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are.
""".trimIndent().replace("\n", " ")
}
}
@Test