Fix serialization of required shapes (timestamps and nested shapes) (#1275)

In #1148, `@required` started being strictly interpreted by server SDKs.

That meant that when serializing values from structures, we no longer
borrow for every shape when looking into `Option`s as in:

```
if let Some(var_37) = &input.value
```

(See for example `RustWriter.serializeStructure` from
`JsonSerializerGenerator.kt` for the relevant serialization
code-generation routine)

Instead, we attempt to serialize the unborrowed required shape value.
This works well for booleans, numeric types (since they are `Copy`),
collections (since we borrow their items while iterating), and strings
(since we call `as_str()`). It also worked for unions and documents,
since we already borrowed (note that `ValueExpression.kt` makes sure to
not borrow twice if the value is already a reference, like when clients
look into `Option`s).

We fixed borrowing of blobs in #1269.

However, we currently don't borrow for timestamps and nested shapes.
This commit fixes that, and adds a comprehensive protocol test to ensure
we exercise all the lines relevant to serialization of required shapes.
This commit is contained in:
david-perez 2022-03-24 04:19:18 -07:00 committed by GitHub
parent 565d838375
commit 2f2e1438f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 9 deletions

View File

@ -15,7 +15,8 @@ service MiscService {
],
}
/// To not regress on https://github.com/awslabs/smithy-rs/pull/1266
/// This operation tests that (de)serializing required values from a nested
/// shape works correctly.
@http(uri: "/operation", method: "GET")
operation OperationWithInnerRequiredShape {
input: OperationWithInnerRequiredShapeInput,
@ -26,13 +27,84 @@ structure OperationWithInnerRequiredShapeInput {
inner: InnerShape
}
structure OperationWithInnerRequiredShapeOutput {
inner: InnerShape
}
structure InnerShape {
@required
requiredInnerMostShape: InnermostShape
}
structure InnermostShape {
aString: String
@required
aString: String,
@required
aBoolean: Boolean,
@required
aByte: Byte,
@required
aShort: Short,
@required
anInt: Integer,
@required
aLong: Long,
@required
aFloat: Float,
@required
aDouble: Double,
// TODO(https://github.com/awslabs/smithy-rs/issues/312)
// @required
// aBigInteger: BigInteger,
// @required
// aBigDecimal: BigDecimal,
@required
aTimestamp: Timestamp,
@required
aDocument: Timestamp,
@required
aStringList: AStringList,
@required
aStringMap: AMap,
@required
aStringSet: AStringSet,
@required
aBlob: Blob,
@required
aUnion: AUnion
}
structure OperationWithInnerRequiredShapeOutput { }
list AStringList {
member: String
}
list AStringSet {
member: String
}
map AMap {
key: String,
value: Timestamp
}
union AUnion {
i32: Integer,
string: String,
time: Timestamp,
}

View File

@ -347,26 +347,26 @@ class JsonSerializerGenerator(
)
}
is BlobShape -> rust(
"$writer.string_unchecked(&#T(${value.name}.as_ref()));",
"$writer.string_unchecked(&#T(${value.asRef()}));",
RuntimeType.Base64Encode(runtimeConfig)
)
is TimestampShape -> {
val timestampFormat =
httpBindingResolver.timestampFormat(context.shape, HttpLocation.DOCUMENT, EPOCH_SECONDS)
val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
rust("$writer.date_time(${value.name}, #T)?;", timestampFormatType)
rust("$writer.date_time(${value.asRef()}, #T)?;", timestampFormatType)
}
is CollectionShape -> jsonArrayWriter(context) { arrayName ->
serializeCollection(Context(arrayName, context.valueExpression, target))
serializeCollection(Context(arrayName, value, target))
}
is MapShape -> jsonObjectWriter(context) { objectName ->
serializeMap(Context(objectName, context.valueExpression, target))
serializeMap(Context(objectName, value, target))
}
is StructureShape -> jsonObjectWriter(context) { objectName ->
serializeStructure(StructContext(objectName, context.valueExpression.name, target))
serializeStructure(StructContext(objectName, value.asRef(), target))
}
is UnionShape -> jsonObjectWriter(context) { objectName ->
serializeUnion(Context(objectName, context.valueExpression, target))
serializeUnion(Context(objectName, value, target))
}
is DocumentShape -> rust("$writer.document(${value.asRef()});")
else -> TODO(target.toString())