Skip to content

Commit e73318e

Browse files
committed
sst constraints,custom_validation_exceptions_experimental test + server-unit-tests works
1 parent 5fa0ff9 commit e73318e

19 files changed

+212
-119
lines changed

codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.D
2020
import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolMetadataProvider
2121
import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolProvider
2222
import software.amazon.smithy.rust.codegen.server.smithy.DeriveEqAndHashSymbolMetadataProvider
23+
import software.amazon.smithy.rust.codegen.server.smithy.customizations.CustomValidationExceptionWithReasonDecorator
2324
import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations
25+
import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionDecorator
2426
import software.amazon.smithy.rust.codegen.server.smithy.customize.CombinedServerCodegenDecorator
2527
import java.util.logging.Level
2628
import java.util.logging.Logger
@@ -47,7 +49,10 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin {
4749
val codegenDecorator: CombinedServerCodegenDecorator =
4850
CombinedServerCodegenDecorator.fromClasspath(
4951
context,
50-
CombinedServerCodegenDecorator(DECORATORS + ServerRequiredCustomizations()),
52+
ServerRequiredCustomizations(),
53+
SmithyValidationExceptionDecorator(),
54+
CustomValidationExceptionWithReasonDecorator(),
55+
*DECORATORS,
5156
)
5257

5358
// PythonServerCodegenVisitor is the main driver of code generation that traverses the model and generates code

codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class PyO3ExtensionModuleDecorator : ServerCodegenDecorator {
134134
}
135135
}
136136

137-
val DECORATORS = listOf(
137+
val DECORATORS = arrayOf(
138138
/**
139139
* Add the [InternalServerError] error to all operations.
140140
* This is done because the Python interpreter can raise exceptions during execution.

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor
2020
import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig
2121
import software.amazon.smithy.rust.codegen.server.smithy.customizations.CustomValidationExceptionWithReasonDecorator
2222
import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations
23+
import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionDecorator
2324
import software.amazon.smithy.rust.codegen.server.smithy.customize.CombinedServerCodegenDecorator
2425
import java.util.logging.Level
2526
import java.util.logging.Logger
@@ -48,6 +49,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin {
4849
CombinedServerCodegenDecorator.fromClasspath(
4950
context,
5051
ServerRequiredCustomizations(),
52+
SmithyValidationExceptionDecorator(),
5153
CustomValidationExceptionWithReasonDecorator(),
5254
)
5355

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedM
6363
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedNumberGenerator
6464
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator
6565
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedTraitForEnumGenerator
66-
import software.amazon.smithy.rust.codegen.server.smithy.generators.CustomValidationExceptionConversionGenerator
66+
import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator
6767
import software.amazon.smithy.rust.codegen.server.smithy.generators.MapConstraintViolationGenerator
6868
import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator
6969
import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator
@@ -103,7 +103,7 @@ open class ServerCodegenVisitor(
103103
protected var codegenContext: ServerCodegenContext
104104
protected var protocolGeneratorFactory: ProtocolGeneratorFactory<ServerProtocolGenerator, ServerCodegenContext>
105105
protected var protocolGenerator: ServerProtocolGenerator
106-
private var customValidationExceptionConversionGenerator: CustomValidationExceptionConversionGenerator?
106+
private var validationExceptionConversionGenerator: ValidationExceptionConversionGenerator
107107

108108
init {
109109
val symbolVisitorConfig =
@@ -147,7 +147,8 @@ open class ServerCodegenVisitor(
147147
serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider,
148148
)
149149

150-
customValidationExceptionConversionGenerator = codegenDecorator.customValidationExceptionConversion(codegenContext)
150+
// We can use a not-null assertion because [CombinedServerCodegenDecorator] returns a not null value.
151+
validationExceptionConversionGenerator = codegenDecorator.validationExceptionConversion(codegenContext)!!
151152

152153
rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, settings.codegenConfig)
153154
protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext)
@@ -200,9 +201,7 @@ open class ServerCodegenVisitor(
200201
"[rust-server-codegen] Generating Rust server for service $service, protocol ${codegenContext.protocol}",
201202
)
202203

203-
val validationExceptionShapeId =
204-
codegenDecorator.customValidationExceptionConversion(codegenContext)?.shapeId
205-
?: ShapeId.from("smithy.framework#ValidationException")
204+
val validationExceptionShapeId = validationExceptionConversionGenerator.shapeId
206205
for (validationResult in listOf(
207206
validateOperationsWithConstrainedInputHaveValidationExceptionAttached(
208207
model,
@@ -269,7 +268,7 @@ open class ServerCodegenVisitor(
269268
writer: RustWriter,
270269
) {
271270
if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) {
272-
val serverBuilderGenerator = ServerBuilderGenerator(codegenContext, shape, customValidationExceptionConversionGenerator)
271+
val serverBuilderGenerator = ServerBuilderGenerator(codegenContext, shape, validationExceptionConversionGenerator)
273272
serverBuilderGenerator.render(writer)
274273

275274
if (codegenContext.settings.codegenConfig.publicConstrainedTypes) {
@@ -290,7 +289,7 @@ open class ServerCodegenVisitor(
290289

291290
if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) {
292291
val serverBuilderGeneratorWithoutPublicConstrainedTypes =
293-
ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape)
292+
ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape, validationExceptionConversionGenerator)
294293
serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer)
295294

296295
writer.implBlock(shape, codegenContext.symbolProvider) {
@@ -459,7 +458,7 @@ open class ServerCodegenVisitor(
459458
} else if (!shape.hasTrait<EnumTrait>() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) {
460459
logger.info("[rust-server-codegen] Generating a constrained string $shape")
461460
rustCrate.withModule(ModelsModule) {
462-
ConstrainedStringGenerator(codegenContext, this, shape, customValidationExceptionConversionGenerator).render()
461+
ConstrainedStringGenerator(codegenContext, this, shape, validationExceptionConversionGenerator).render()
463462
}
464463
}
465464
}

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/CustomValidationExceptionWithReasonDecorator.kt

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
88
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
99
import software.amazon.smithy.rust.codegen.core.rustlang.writable
1010
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
11-
import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE
1211
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext
1312
import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType
1413
import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator
15-
import software.amazon.smithy.rust.codegen.server.smithy.generators.CollectionTraitInfo
1614
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstraintViolation
17-
import software.amazon.smithy.rust.codegen.server.smithy.generators.CustomValidationExceptionConversionGenerator
15+
import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator
1816
import software.amazon.smithy.rust.codegen.server.smithy.generators.Length
1917
import software.amazon.smithy.rust.codegen.server.smithy.generators.Pattern
2018
import software.amazon.smithy.rust.codegen.server.smithy.generators.StringTraitInfo
21-
import software.amazon.smithy.rust.codegen.server.smithy.generators.TraitInfo
2219
import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage
2320

2421
// TODO Docs
@@ -28,18 +25,18 @@ class CustomValidationExceptionWithReasonDecorator: ServerCodegenDecorator {
2825
override val order: Byte
2926
get() = -69
3027

31-
override fun customValidationExceptionConversion(codegenContext: ServerCodegenContext):
32-
CustomValidationExceptionConversionGenerator? =
28+
override fun validationExceptionConversion(codegenContext: ServerCodegenContext):
29+
ValidationExceptionConversionGenerator? =
3330
if (codegenContext.settings.codegenConfig.experimentalCustomValidationExceptionWithReasonPleaseDoNotUse != null) {
34-
CustomValidationExceptionWithReasonConversionGenerator(codegenContext)
31+
ValidationExceptionWithReasonConversionGenerator(codegenContext)
3532
} else {
3633
null
3734
}
3835
}
3936

4037
// TODO Docs
41-
class CustomValidationExceptionWithReasonConversionGenerator(private val codegenContext: ServerCodegenContext):
42-
CustomValidationExceptionConversionGenerator {
38+
class ValidationExceptionWithReasonConversionGenerator(private val codegenContext: ServerCodegenContext):
39+
ValidationExceptionConversionGenerator {
4340
override val shapeId: ShapeId =
4441
ShapeId.from(codegenContext.settings.codegenConfig.experimentalCustomValidationExceptionWithReasonPleaseDoNotUse)
4542

@@ -69,9 +66,9 @@ class CustomValidationExceptionWithReasonConversionGenerator(private val codegen
6966
)
7067
}
7168

72-
override fun stringShapeConstraintViolationImplBlock(constraintsInfo: Collection<StringTraitInfo>): Writable = writable {
69+
override fun stringShapeConstraintViolationImplBlock(stringConstraintsInfo: Collection<StringTraitInfo>): Writable = writable {
7370
val validationExceptionFields =
74-
constraintsInfo.map {
71+
stringConstraintsInfo.map {
7572
writable {
7673
when (it) {
7774
is Pattern -> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package software.amazon.smithy.rust.codegen.server.smithy.customizations
2+
3+
import software.amazon.smithy.model.shapes.ShapeId
4+
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
5+
import software.amazon.smithy.rust.codegen.core.rustlang.join
6+
import software.amazon.smithy.rust.codegen.core.rustlang.rust
7+
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
8+
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
9+
import software.amazon.smithy.rust.codegen.core.rustlang.writable
10+
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
11+
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext
12+
import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType
13+
import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator
14+
import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstraintViolation
15+
import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator
16+
import software.amazon.smithy.rust.codegen.server.smithy.generators.StringTraitInfo
17+
import software.amazon.smithy.rust.codegen.server.smithy.generators.TraitInfo
18+
19+
// TODO Docs
20+
class SmithyValidationExceptionDecorator: ServerCodegenDecorator {
21+
override val name: String
22+
get() = "SmithyValidationExceptionDecorator"
23+
override val order: Byte
24+
get() = 69
25+
26+
override fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator =
27+
SmithyValidationExceptionConversionGenerator(codegenContext)
28+
}
29+
30+
// TODO Docs
31+
class SmithyValidationExceptionConversionGenerator(private val codegenContext: ServerCodegenContext):
32+
ValidationExceptionConversionGenerator {
33+
34+
// Define a companion object so that we can refer to this shape id globally.
35+
companion object {
36+
val SHAPE_ID: ShapeId = ShapeId.from("smithy.framework#ValidationException")
37+
}
38+
override val shapeId: ShapeId = SHAPE_ID
39+
40+
override fun renderImplFromConstraintViolationForRequestRejection(): Writable = writable {
41+
val codegenScope = arrayOf(
42+
"RequestRejection" to ServerRuntimeType.requestRejection(codegenContext.runtimeConfig),
43+
"From" to RuntimeType.From,
44+
)
45+
rustTemplate(
46+
"""
47+
impl #{From}<ConstraintViolation> for #{RequestRejection} {
48+
fn from(constraint_violation: ConstraintViolation) -> Self {
49+
let first_validation_exception_field = constraint_violation.as_validation_exception_field("".to_owned());
50+
let validation_exception = crate::error::ValidationException {
51+
message: format!("1 validation error detected. {}", &first_validation_exception_field.message),
52+
field_list: Some(vec![first_validation_exception_field]),
53+
};
54+
Self::ConstraintViolation(
55+
crate::operation_ser::serialize_structure_crate_error_validation_exception(&validation_exception)
56+
.expect("validation exceptions should never fail to serialize; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
57+
)
58+
}
59+
}
60+
""",
61+
*codegenScope,
62+
)
63+
}
64+
65+
override fun stringShapeConstraintViolationImplBlock(stringConstraintsInfo: Collection<StringTraitInfo>): Writable = writable {
66+
val constraintsInfo: List<TraitInfo> =
67+
stringConstraintsInfo
68+
.map(StringTraitInfo::toTraitInfo)
69+
rustTemplate(
70+
"""
71+
pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField {
72+
match self {
73+
#{ValidationExceptionFields:W}
74+
}
75+
}
76+
""",
77+
"String" to RuntimeType.String,
78+
"ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"),
79+
)
80+
}
81+
82+
override fun builderConstraintViolationImplBlock(constraintViolations: Collection<ConstraintViolation>) = writable {
83+
rustBlock("match self") {
84+
constraintViolations.forEach {
85+
if (it.hasInner()) {
86+
rust("""ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field(path + "/${it.forMember.memberName}"),""")
87+
} else {
88+
rust(
89+
"""
90+
ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField {
91+
message: format!("Value null at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path),
92+
path: path + "/${it.forMember.memberName}",
93+
},
94+
""",
95+
)
96+
}
97+
}
98+
}
99+
}
100+
}

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCod
1111
import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator
1212
import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap
1313
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext
14-
import software.amazon.smithy.rust.codegen.server.smithy.generators.CustomValidationExceptionConversionGenerator
14+
import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator
1515
import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator
1616
import java.util.logging.Logger
1717

@@ -24,7 +24,7 @@ interface ServerCodegenDecorator : CoreCodegenDecorator<ServerCodegenContext> {
2424
fun protocols(serviceId: ShapeId, currentProtocols: ServerProtocolMap): ServerProtocolMap = currentProtocols
2525

2626
// TODO Docs
27-
fun customValidationExceptionConversion(codegenContext: ServerCodegenContext): CustomValidationExceptionConversionGenerator? = null
27+
fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator? = null
2828
}
2929

3030
/**
@@ -45,9 +45,10 @@ class CombinedServerCodegenDecorator(private val decorators: List<ServerCodegenD
4545
decorator.protocols(serviceId, protocolMap)
4646
}
4747

48-
override fun customValidationExceptionConversion(codegenContext: ServerCodegenContext):
49-
CustomValidationExceptionConversionGenerator? =
50-
decorators.sortedBy { it.order }.firstNotNullOfOrNull { it.customValidationExceptionConversion(codegenContext) }
48+
override fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator =
49+
// We use `firstNotNullOf` instead of `firstNotNullOfOrNull` because the [SmithyValidationExceptionDecorator]
50+
// is registered.
51+
decorators.sortedBy { it.order }.firstNotNullOf { it.validationExceptionConversion(codegenContext) }
5152

5253
companion object {
5354
fun fromClasspath(

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ConstrainedStringGenerator(
4747
val codegenContext: ServerCodegenContext,
4848
val writer: RustWriter,
4949
val shape: StringShape,
50-
val customValidationExceptionConversionGenerator: CustomValidationExceptionConversionGenerator? = null,
50+
val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator,
5151
) {
5252
val model = codegenContext.model
5353
val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider
@@ -155,30 +155,14 @@ class ConstrainedStringGenerator(
155155
)
156156

157157
if (shape.isReachableFromOperationInput()) {
158-
if (customValidationExceptionConversionGenerator != null) {
159-
writer.rustTemplate(
160-
"""
161-
impl ${constraintViolation.name} {
162-
#{StringShapeConstraintViolationImplBlock:W}
163-
}
164-
""",
165-
"StringShapeConstraintViolationImplBlock" to customValidationExceptionConversionGenerator.stringShapeConstraintViolationImplBlock(stringConstraintsInfo)
166-
)
167-
} else {
168-
writer.rustTemplate(
169-
"""
170-
impl ${constraintViolation.name} {
171-
pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField {
172-
match self {
173-
#{ValidationExceptionFields:W}
174-
}
175-
}
176-
}
177-
""",
178-
"String" to RuntimeType.String,
179-
"ValidationExceptionFields" to constraintsInfo.map { it.asValidationExceptionField }.join("\n"),
180-
)
181-
}
158+
writer.rustTemplate(
159+
"""
160+
impl ${constraintViolation.name} {
161+
#{StringShapeConstraintViolationImplBlock:W}
162+
}
163+
""",
164+
"StringShapeConstraintViolationImplBlock" to validationExceptionConversionGenerator.stringShapeConstraintViolationImplBlock(stringConstraintsInfo)
165+
)
182166
}
183167
}
184168

0 commit comments

Comments
 (0)