Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade Smithy to 1.16.1 #1053

Merged
merged 11 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ message = "Add support for SSO credentials"
references = ["smithy-rs#1051", "aws-sdk-rust#4"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "rcoh"

[[smithy-rs]]
message = "Upgraded Smithy to 1.16.1"
references = ["smithy-rs#1053"]
meta = { "breaking" = false, "tada" = false, "bug" = false }
author = "jdisanti"
4 changes: 0 additions & 4 deletions aws/sdk-codegen-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ plugins {

val smithyVersion: String by project


dependencies {
implementation(project(":aws:sdk-codegen"))
implementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion")
Expand Down Expand Up @@ -55,15 +54,13 @@ fun generateSmithyBuild(tests: List<CodegenTest>): String {
"""
}


task("generateSmithyBuild") {
description = "generate smithy-build.json"
doFirst {
projectDir.resolve("smithy-build.json").writeText(generateSmithyBuild(CodegenTests))
}
}


fun generateCargoWorkspace(tests: List<CodegenTest>): String {
return """
[workspace]
Expand All @@ -82,7 +79,6 @@ task("generateCargoWorkspace") {
tasks["smithyBuildJar"].dependsOn("generateSmithyBuild")
tasks["assemble"].finalizedBy("generateCargoWorkspace")


tasks.register<Exec>("cargoCheck") {
workingDir("build/smithyprojections/sdk-codegen-test/")
// disallow warnings
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies {
}

val lintPaths = listOf(
"codegen/src/**/*.kt"
"codegen/src/**/*.kt"
)

tasks.register<JavaExec>("ktlint") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,22 @@ class ServerProtocolTestGenerator(
// This function applies a "fix function" to each broken test before we synthesize it.
// Broken tests are those whose definitions in the `awslabs/smithy` repository are wrong, usually because they have
// not been written with a server-side perspective in mind.
private fun List<TestCase>.fixBroken(): List<TestCase> = this.map { when (it) {
is TestCase.RequestTest -> {
val howToFixIt = BrokenRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.testCase.id)]
if (howToFixIt == null) {
private fun List<TestCase>.fixBroken(): List<TestCase> = this.map {
when (it) {
is TestCase.RequestTest -> {
val howToFixIt = BrokenRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.testCase.id)]
if (howToFixIt == null) {
it
} else {
val fixed = howToFixIt(it.testCase)
TestCase.RequestTest(fixed, it.targetShape)
}
}
is TestCase.ResponseTest -> {
it
} else {
val fixed = howToFixIt(it.testCase)
TestCase.RequestTest(fixed, it.targetShape)
}
}
is TestCase.ResponseTest -> {
it
}
} }
}

private fun renderTestCaseBlock(
testCase: HttpMessageTestCase,
Expand Down Expand Up @@ -431,6 +433,11 @@ class ServerProtocolTestGenerator(
private val AwsQuery = "aws.protocoltests.query#AwsQuery"
private val Ec2Query = "aws.protocoltests.ec2#AwsEc2"
private val ExpectFail = setOf<FailingTest>(
FailingTest(RestJson, "RestJsonInputAndOutputWithQuotedStringHeaders", Action.Request),
FailingTest(RestJson, "RestJsonInputAndOutputWithQuotedStringHeaders", Action.Response),
FailingTest(RestJson, "RestJsonOutputUnionWithUnitMember", Action.Response),
FailingTest(RestJson, "RestJsonUnitInputAllowsAccept", Action.Request),
FailingTest(RestJson, "RestJsonUnitInputAndOutputNoOutput", Action.Response),
FailingTest(RestJson, "RestJsonAllQueryStringTypes", Action.Request),
FailingTest(RestJson, "RestJsonQueryStringEscaping", Action.Request),
FailingTest(RestJson, "RestJsonSupportsNaNFloatQueryValues", Action.Request),
Expand Down Expand Up @@ -572,38 +579,50 @@ class ServerProtocolTestGenerator(
// to any "expected" value.
// Reference: https://doc.rust-lang.org/std/primitive.f32.html
// Request for guidance about this test to Smithy team: https://github.com/awslabs/smithy/pull/1040#discussion_r780418707
val params = Node.parse("""{
"queryFloat": "NaN",
"queryDouble": "NaN",
"queryParamsMapOfStringList": {
"Float": ["NaN"],
"Double": ["NaN"]
val params = Node.parse(
"""
{
"queryFloat": "NaN",
"queryDouble": "NaN",
"queryParamsMapOfStringList": {
"Float": ["NaN"],
"Double": ["NaN"]
}
}
}""".trimIndent()).asObjectNode().get()
""".trimIndent()
).asObjectNode().get()

return testCase.toBuilder().params(params).build()
}
private fun fixRestJsonSupportsInfinityFloatQueryValues(testCase: HttpRequestTestCase): HttpRequestTestCase =
testCase.toBuilder().params(
Node.parse("""{
"queryFloat": "Infinity",
"queryDouble": "Infinity",
"queryParamsMapOfStringList": {
"Float": ["Infinity"],
"Double": ["Infinity"]
}
}""".trimMargin()).asObjectNode().get()
Node.parse(
"""
{
"queryFloat": "Infinity",
"queryDouble": "Infinity",
"queryParamsMapOfStringList": {
"Float": ["Infinity"],
"Double": ["Infinity"]
}
}
""".trimMargin()
).asObjectNode().get()
).build()
private fun fixRestJsonSupportsNegativeInfinityFloatQueryValues(testCase: HttpRequestTestCase): HttpRequestTestCase =
testCase.toBuilder().params(
Node.parse("""{
"queryFloat": "-Infinity",
"queryDouble": "-Infinity",
"queryParamsMapOfStringList": {
"Float": ["-Infinity"],
"Double": ["-Infinity"]
}
}""".trimMargin()).asObjectNode().get()
Node.parse(
"""
{
"queryFloat": "-Infinity",
"queryDouble": "-Infinity",
"queryParamsMapOfStringList": {
"Float": ["-Infinity"],
"Double": ["-Infinity"]
}
}
""".trimMargin()
).asObjectNode().get()
).build()

// These are tests whose definitions in the `awslabs/smithy` repository are wrong.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolContentTypes
import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory
import software.amazon.smithy.rust.codegen.smithy.protocols.parse.JsonParserGenerator
import software.amazon.smithy.rust.codegen.smithy.protocols.parse.StructuredDataParserGenerator
import software.amazon.smithy.rust.codegen.smithy.protocols.restJsonFieldName
import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.JsonSerializerGenerator
import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.StructuredDataSerializerGenerator

Expand Down Expand Up @@ -75,11 +76,11 @@ class ServerRestJson(private val codegenContext: CodegenContext) : Protocol {
override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.EPOCH_SECONDS

override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator {
return JsonParserGenerator(codegenContext, httpBindingResolver)
return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName)
}

override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator {
return JsonSerializerGenerator(codegenContext, httpBindingResolver)
return JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName)
}

// NOTE: this method is only needed for the little part of client-codegen we use in tests.
Expand Down
42 changes: 42 additions & 0 deletions codegen-test/model/rest-json-extras.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,46 @@ use aws.api#service
use smithy.test#httpRequestTests
use smithy.test#httpResponseTests

// TODO(https://github.com/awslabs/smithy/pull/1049): Remove this once the test case in Smithy is fixed
apply InputAndOutputWithHeaders @httpResponseTests([
{
id: "FIXED_RestJsonInputAndOutputWithQuotedStringHeaders",
documentation: "Tests responses with string list header bindings that require quoting",
protocol: restJson1,
code: 200,
headers: {
"X-StringList": "\"b,c\", \"\\\"def\\\"\", a"
},
params: {
headerStringList: ["b,c", "\"def\"", "a"]
}
}
])

// TODO(https://github.com/awslabs/smithy/pull/1042): Remove this once the test case in Smithy is fixed
apply PostPlayerAction @httpRequestTests([
{
id: "FIXED_RestJsonInputUnionWithUnitMember",
documentation: "Unit types in unions are serialized like normal structures in requests.",
protocol: restJson1,
method: "POST",
"uri": "/PostPlayerInput",
body: """
{
"action": {
"quit": {}
}
}""",
bodyMediaType: "application/json",
headers: {"Content-Type": "application/json"},
params: {
action: {
quit: {}
}
}
}
])

apply QueryPrecedence @httpRequestTests([
{
id: "UrlParamsKeyEncoding",
Expand Down Expand Up @@ -64,6 +104,8 @@ service RestJsonExtras {
NullInNonSparse,
CaseInsensitiveErrorOperation,
EmptyStructWithContentOnWireOp,
// TODO(https://github.com/awslabs/smithy/pull/1042): Remove this once the test case in Smithy is fixed
PostPlayerAction
],
errors: [ExtraError]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class RequestBindingGenerator(
) {
private val index = HttpBindingIndex.of(model)
private val Encoder = CargoDependency.SmithyTypes(runtimeConfig).asType().member("primitive::Encoder")
private val headerUtil = CargoDependency.SmithyHttp(runtimeConfig).asType().member("header")

private val codegenScope = arrayOf(
"BuildError" to runtimeConfig.operationBuildError(),
Expand Down Expand Up @@ -175,6 +176,7 @@ class RequestBindingGenerator(
else -> UNREACHABLE("unexpected member for prefix headers: $memberType")
}
ifSet(memberType, memberSymbol, "&_input.$memberName") { field ->
val listHeader = memberType is CollectionShape
rustTemplate(
"""
for (k, v) in $field {
Expand All @@ -183,8 +185,8 @@ class RequestBindingGenerator(
#{build_error}::InvalidField { field: ${memberName.dq()}, details: format!("`{}` cannot be used as a header name: {}", k, err)}
})?;
use std::convert::TryFrom;
let header_value = ${headerFmtFun(this, target, memberShape, "v")};
let header_value = http::header::HeaderValue::try_from(header_value).map_err(|err| {
let header_value = ${headerFmtFun(this, target, memberShape, "v", listHeader)};
let header_value = http::header::HeaderValue::try_from(&*header_value).map_err(|err| {
#{build_error}::InvalidField {
field: ${memberName.dq()},
details: format!("`{}` cannot be used as a header value: {}", ${
Expand All @@ -210,12 +212,13 @@ class RequestBindingGenerator(
val memberSymbol = symbolProvider.toSymbol(memberShape)
val memberName = symbolProvider.toMemberName(memberShape)
ifSet(memberType, memberSymbol, "&_input.$memberName") { field ->
val isListHeader = memberType is CollectionShape
listForEach(memberType, field) { innerField, targetId ->
val innerMemberType = model.expectShape(targetId)
if (innerMemberType.isPrimitive()) {
rust("let mut encoder = #T::from(${autoDeref(innerField)});", Encoder)
}
val formatted = headerFmtFun(this, innerMemberType, memberShape, innerField)
val formatted = headerFmtFun(this, innerMemberType, memberShape, innerField, isListHeader)
val safeName = safeName("formatted")
write("let $safeName = $formatted;")
rustBlock("if !$safeName.is_empty()") {
Expand Down Expand Up @@ -244,21 +247,30 @@ class RequestBindingGenerator(
/**
* Format [member] in the when used as an HTTP header
*/
private fun headerFmtFun(writer: RustWriter, target: Shape, member: MemberShape, targetName: String): String {
private fun headerFmtFun(writer: RustWriter, target: Shape, member: MemberShape, targetName: String, isListHeader: Boolean): String {
fun quoteValue(value: String): String {
// Timestamp shapes are not quoted in header lists
return if (isListHeader && !target.isTimestampShape) {
val quoteFn = writer.format(headerUtil.member("quote_header_value"))
"$quoteFn($value)"
} else {
value
}
}
return when {
target.isStringShape -> {
if (target.hasTrait<MediaTypeTrait>()) {
val func = writer.format(RuntimeType.Base64Encode(runtimeConfig))
"$func(&$targetName)"
} else {
"AsRef::<str>::as_ref($targetName)"
quoteValue("AsRef::<str>::as_ref($targetName)")
}
}
target.isTimestampShape -> {
val timestampFormat =
index.determineTimestampFormat(member, HttpBinding.Location.HEADER, defaultTimestampFormat)
val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat)
"$targetName.fmt(${writer.format(timestampFormatType)})?"
quoteValue("$targetName.fmt(${writer.format(timestampFormatType)})?")
}
target.isListShape || target.isMemberShape -> {
throw IllegalArgumentException("lists should be handled at a higher level")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,9 @@ class ProtocolTestGenerator(
rust(
"""
assert_eq!(
parsed.$memberName.collect().await.unwrap().into_bytes(),
expected_output.$memberName.collect().await.unwrap().into_bytes()
);
parsed.$memberName.collect().await.unwrap().into_bytes(),
expected_output.$memberName.collect().await.unwrap().into_bytes()
);
"""
)
} else {
Expand Down Expand Up @@ -367,7 +367,7 @@ class ProtocolTestGenerator(
}
val variableName = "expected_headers"
rustWriter.withBlock("let $variableName = [", "];") {
write(
writeWithNoFormatting(
headers.entries.joinToString(",") {
"(${it.key.dq()}, ${it.value.dq()})"
}
Expand Down Expand Up @@ -450,7 +450,13 @@ class ProtocolTestGenerator(
private val RestXml = "aws.protocoltests.restxml#RestXml"
private val AwsQuery = "aws.protocoltests.query#AwsQuery"
private val Ec2Query = "aws.protocoltests.ec2#AwsEc2"
private val ExpectFail = setOf<FailingTest>()
private val ExpectFail = setOf<FailingTest>(
// TODO(https://github.com/awslabs/smithy/pull/1049): Remove this once the test case in Smithy is fixed
FailingTest(RestJson, "RestJsonInputAndOutputWithQuotedStringHeaders", Action.Response),
// TODO(https://github.com/awslabs/smithy/pull/1042): Remove this once the test case in Smithy is fixed
FailingTest(RestJson, "RestJsonInputUnionWithUnitMember", Action.Request),
FailingTest("${RestJson}Extras", "RestJsonInputUnionWithUnitMember", Action.Request),
)
private val RunOnly: Set<String>? = null

// These tests are not even attempted to be generated, either because they will not compile
Expand Down
Loading