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

feat: Add support for OperationContextParams #1680

Merged
merged 11 commits into from
Aug 21, 2024
Merged
1 change: 1 addition & 0 deletions codegen/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func addProtocolTests() {
.init(name: "EventStream", sourcePath: "\(baseDirLocal)/EventStream", buildOnly: true),
.init(name: "RPCEventStream", sourcePath: "\(baseDirLocal)/RPCEventStream", buildOnly: true),
.init(name: "Waiters", sourcePath: "\(baseDirLocal)/Waiters", testPath: "../codegen/protocol-test-codegen-local/Tests"),
.init(name: "StringArrayEndpointParam", sourcePath: "\(baseDirLocal)/StringArrayEndpointParam")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add new target for running runtime test that run against generated code

]
for protocolTest in protocolTests {
let target = Target.target(
Expand Down
4 changes: 4 additions & 0 deletions codegen/protocol-test-codegen-local/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ val codegenTests = listOf(
CodegenTest(
"aws.protocoltests.eventstream#RPCTestService",
"RPCEventStream"
),
CodegenTest(
"aws.endpointtests.stringarray#EndpointStringArray",
"StringArrayEndpointParam"
Comment on lines +51 to +53
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the endpoint string array Smithy model to list of models used by this build script

)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
$version: "2.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the Smithy model file provided by SEP that has prescribed test cases. Only edits made were putting on @service trait and @restJson1 trait to the service shape, and adding @http traits to each of the operation shape with dummy values. This was done to "plug" this into existing protocol test codegen flow. Building the local protocol test gradle project generates Swift service code for this model, along with the endpoint tests.


namespace aws.endpointtests.stringarray

use smithy.rules#endpointRuleSet
use smithy.rules#endpointTests
use smithy.rules#staticContextParams
use smithy.rules#operationContextParams
use aws.api#service
use aws.protocols#restJson1

@endpointRuleSet({
version: "1.0",
parameters: {
stringArrayParam: {
type: "stringArray",
required: true,
default: ["defaultValue1", "defaultValue2"],
documentation: "docs"
}
},
rules: [
{
"documentation": "Template first array value into URI if set",
"conditions": [
{
"fn": "getAttr",
"argv": [
{
"ref": "stringArrayParam"
},
"[0]"
],
"assign": "arrayValue"
}
],
"endpoint": {
"url": "https://example.com/{arrayValue}"
},
"type": "endpoint"
},
{
"conditions": [],
"documentation": "error fallthrough",
"error": "no array values set",
"type": "error"
}
]
})
@endpointTests({
"version": "1.0",
"testCases": [
{
"documentation": "Default array values used"
"params": {}
"expect": {
"endpoint": {
"url": "https://example.com/defaultValue1"
}
},
"operationInputs": [
{
"operationName": "NoBindingsOperation",
}
]
},
{
"documentation": "Empty array",
"params": {
"stringArrayParam": []
}
"expect": {
"error": "no array values set"
},
"operationInputs": [
{
"operationName": "EmptyStaticContextOperation",
}
]
},
{
"documentation": "Static value",
"params": {
"stringArrayParam": ["staticValue1"]
}
"expect": {
"endpoint": {
"url": "https://example.com/staticValue1"
}
},
"operationInputs": [
{
"operationName": "StaticContextOperation",
}
]
},
{
"documentation": "bound value from input",
"params": {
"stringArrayParam": ["key1"]
}
"expect": {
"endpoint": {
"url": "https://example.com/key1"
}
},
"operationInputs": [
{
"operationName": "ListOfObjectsOperation",
"operationParams": {
"nested": {
"listOfObjects": [{"key": "key1"}]
}
},
},
{
"operationName": "MapOperation",
"operationParams": {
"map": {
"key1": "value1"
}
}
}
]
}
]
})
@service(sdkId: "EndpointStringArray")
@restJson1
service EndpointStringArray {
version: "2022-01-01",
operations: [
NoBindingsOperation,
EmptyStaticContextOperation,
StaticContextOperation,
ListOfObjectsOperation,
MapOperation
]
}

@http(uri: "/1", method: "POST")
operation NoBindingsOperation {
input:= {}
}

@staticContextParams(
"stringArrayParam": {value: []}
)
@http(uri: "/2", method: "POST")
operation EmptyStaticContextOperation {
input := {}
}

@staticContextParams(
"stringArrayParam": {value: ["staticValue1"]}
)
@http(uri: "/3", method: "POST")
operation StaticContextOperation {
input := {}
}

@operationContextParams(
"stringArrayParam": {path: "nested.listOfObjects[*].key"}
)
@http(uri: "/4", method: "POST")
operation ListOfObjectsOperation {
input:= {
nested: Nested
}
}

@operationContextParams(
"stringArrayParam": {path: "keys(map)"}
)
@http(uri: "/5", method: "POST")
operation MapOperation {
input:= {
map: Map
}
}

structure Nested {
listOfObjects: ListOfObjects
}

list ListOfObjects {
member: ObjectMember
}

structure ObjectMember {
key: String,
}

map Map {
key: String,
value: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class EndpointTestGenerator(
val value = Value.fromNode(pair.second)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in this file fixes a bug in endpoint test generator where it was incorrectly & unnecessarily casting array endpoint params to [AnyHashable]. This was never caught before bc we never had test for it & no service has string array endpoint params yet.

writer.call {
generateValue(
writer, value, if (idx < applicableParams.count() - 1) "," else ""
writer, value, if (idx < applicableParams.count() - 1) "," else "", false
)
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ class EndpointTestGenerator(
val value = Value.fromNode(second)
writer.writeInline("\$S: ", first)
writer.call {
generateValue(writer, value, if (idx < properties.values.count() - 1) "," else "")
generateValue(writer, value, if (idx < properties.values.count() - 1) "," else "", true)
}
}
}
Expand All @@ -137,7 +137,7 @@ class EndpointTestGenerator(
/**
* Recursively traverse the value and render a JSON string literal.
*/
private fun generateValue(writer: SwiftWriter, value: Value, delimeter: String) {
private fun generateValue(writer: SwiftWriter, value: Value, delimeter: String, castToAnyHashable: Boolean) {
when (value) {
is StringValue -> {
writer.write("\$S$delimeter", value.toString())
Expand All @@ -156,10 +156,11 @@ class EndpointTestGenerator(
}

is ArrayValue -> {
writer.openBlock("[", "] as [AnyHashable]$delimeter") {
val castStmt = if (castToAnyHashable) " as [AnyHashable]$delimeter" else ""
writer.openBlock("[", "]$castStmt") {
value.values.forEachIndexed { idx, item ->
writer.call {
generateValue(writer, item, if (idx < value.values.count() - 1) "," else "")
generateValue(writer, item, if (idx < value.values.count() - 1) "," else "", castToAnyHashable)
}
}
}
Expand All @@ -173,7 +174,7 @@ class EndpointTestGenerator(
value.value.map { it.key to it.value }.forEachIndexed { idx, (first, second) ->
writer.writeInline("\$S: ", first.name)
writer.call {
generateValue(writer, second, if (idx < value.value.count() - 1) "," else "")
generateValue(writer, second, if (idx < value.value.count() - 1) "," else "", castToAnyHashable)
}
}
}
Expand Down
Loading
Loading