diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..185f74e5a06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +/dist +/ideaSDK +/android-studio/sdk +out/ +/tmp +workspace.xml +*.versionsBackup +.gradle/ +build/ +*.iml +.idea/ +local.properties diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000000..34efc5e4d84 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +plugins { + kotlin("jvm") version "1.3.72" apply false +} + +allprojects { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + +val ktlint by configurations.creating +val ktlintVersion: String by project +dependencies { + ktlint("com.pinterest:ktlint:$ktlintVersion") +} + +val lintPaths = listOf( + "codegen/**/*.kt" +) + +tasks.register<JavaExec>("ktlint") { + description = "Check Kotlin code style." + group = "Verification" + classpath = configurations.getByName("ktlint") + main = "com.pinterest.ktlint.Main" + args = lintPaths +} + +//tasks.named("check") { +// dependsOn(":ktlint") +//} + +tasks.register<JavaExec>("ktlintFormat") { + description = "Auto fix Kotlin code style violations" + group = "formatting" + classpath = configurations.getByName("ktlint") + main = "com.pinterest.ktlint.Main" + args = listOf("-F") + lintPaths +} diff --git a/codegen/protocol-test-codegen/build.gradle.kts b/codegen/protocol-test-codegen/build.gradle.kts new file mode 100644 index 00000000000..3d5c02ffe81 --- /dev/null +++ b/codegen/protocol-test-codegen/build.gradle.kts @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import software.amazon.smithy.gradle.tasks.SmithyBuild + +plugins { + id("software.amazon.smithy") version "0.5.0" +} + +dependencies { + implementation("software.amazon.smithy:smithy-aws-protocol-tests:1.0.5") + compile(project(":smithy-aws-swift-codegen")) +} + +// This project doesn't produce a JAR. +tasks["jar"].enabled = false + +// Run the SmithyBuild task manually since this project needs the built JAR +// from smithy-aws-swift-codegen. +tasks["smithyBuildJar"].enabled = false + +tasks.create<SmithyBuild>("buildSdk") { + addRuntimeClasspath = true +} + +// Run the `buildSdk` automatically. +tasks["build"].finalizedBy(tasks["buildSdk"]) + +// TODO:: ensure built artifacts are put into the SDK's folders \ No newline at end of file diff --git a/codegen/protocol-test-codegen/smithy-build.json b/codegen/protocol-test-codegen/smithy-build.json new file mode 100644 index 00000000000..c8b6cd7c843 --- /dev/null +++ b/codegen/protocol-test-codegen/smithy-build.json @@ -0,0 +1,26 @@ +{ + "version": "1.0", + "projections": { + "aws-restjson": { + "transforms": [ + { + "name": "includeServices", + "args": { + "services": ["aws.protocoltests.restjson#RestJson"] + } + } + ], + "plugins": { + "swift-codegen": { + "service": "aws.protocoltests.restjson#RestJson", + "module": "AWSRestJsonTestSDK", + "moduleVersion": "1.0", + "gitRepo": "https://github.com/aws-amplify/amplify-codegen.git", + "author": "Amazon Web Services", + "homepage": "https://docs.amplify.aws/", + "swiftVersion": "5.1.0" + } + } + } + } +} diff --git a/codegen/smithy-aws-swift-codegen/build.gradle.kts b/codegen/smithy-aws-swift-codegen/build.gradle.kts new file mode 100644 index 00000000000..d3031d8d608 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/build.gradle.kts @@ -0,0 +1,72 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +plugins { + kotlin("jvm") + jacoco +} + +//description = "Generates Swift code from Smithy models" +//extra["displayName"] = "AWS:: Smithy :: Swift :: Codegen" +//extra["moduleName"] = "software.amazon.smithy.aws.swift.codegen" + +group = "software.amazon.smithy" +version = "0.1.0" + +val smithyVersion: String by project +val kotestVersion: String by project + +dependencies { + implementation(kotlin("stdlib-jdk8")) + api("software.amazon.smithy:smithy-swift-codegen:0.1.0") + api("software.amazon.smithy:smithy-aws-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + testImplementation("org.junit.jupiter:junit-jupiter:5.6.1") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") +} + +// Reusable license copySpec +val licenseSpec = copySpec { + from("${project.rootDir}/LICENSE") + from("${project.rootDir}/NOTICE") +} + +// Configure jars to include license related info +tasks.jar { + metaInf.with(licenseSpec) + inputs.property("moduleName", project.name) + manifest { + attributes["Automatic-Module-Name"] = project.name + } +} + +tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + } +} + +// Configure jacoco (code coverage) to generate an HTML report +tasks.jacocoTestReport { + reports { + xml.isEnabled = false + csv.isEnabled = false + html.destination = file("$buildDir/reports/jacoco") + } +} + +// Always run the jacoco test report after testing. +tasks["test"].finalizedBy(tasks["jacocoTestReport"]) diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt new file mode 100644 index 00000000000..e868e5d7fb3 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.model.shapes.ShapeId + +/** + * Handles generating the aws.rest-json protocol for services. + * + * @inheritDoc + * @see RestJsonProtocolGenerator + */ + +class AWSRestJson1ProtocolGenerator : RestJsonProtocolGenerator() { + override val defaultContentType: String = "application/json" + override val protocol: ShapeId = RestJson1Trait.ID +} diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt new file mode 100644 index 00000000000..27698a18573 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package software.amazon.smithy.aws.swift.codegen + + +import software.amazon.smithy.swift.codegen.integration.SwiftIntegration +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator + +/** + * Integration that registers protocol generators this package provides + */ +class AddProtocols : SwiftIntegration { + /** + * Gets the sort order of the customization from -128 to 127, with lowest + * executed first. + * + * @return Returns the sort order, defaults to -10. + */ + override val order: Byte = -10 + + override val protocolGenerators: List<ProtocolGenerator> = listOf(AWSRestJson1ProtocolGenerator()) +} \ No newline at end of file diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt new file mode 100644 index 00000000000..2d2556733e9 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt @@ -0,0 +1,113 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package software.amazon.smithy.aws.swift.codegen + +import software.amazon.smithy.model.knowledge.HttpBinding +import software.amazon.smithy.model.knowledge.HttpBindingIndex +import software.amazon.smithy.model.knowledge.OperationIndex +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.swift.codegen.ServiceGenerator +import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGenerator +import software.amazon.smithy.swift.codegen.integration.HttpFeature +import software.amazon.smithy.swift.codegen.integration.HttpRequestEncoder +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator + + +/** + * Shared base protocol generator for all AWS JSON protocol variants + */ +abstract class RestJsonProtocolGenerator : HttpBindingProtocolGenerator() { + + override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext) {} + + override fun generateSerializers(ctx: ProtocolGenerator.GenerationContext) { + // Generate extension on input requests to implement Codable protocol + val inputShapesWithHttpBindings:MutableSet<ShapeId> = mutableSetOf() + for (operation in getHttpBindingOperations(ctx)) { + if (operation.input.isPresent) { + val inputShapeId = operation.input.get() + if (inputShapesWithHttpBindings.contains(inputShapeId)) { + // The input shape is referenced by more than one operation + continue + } + renderInputRequestConformanceToCodable(ctx, operation) + inputShapesWithHttpBindings.add(inputShapeId) + } + } + super.generateSerializers(ctx) + } + + override fun getHttpFeatures(ctx: ProtocolGenerator.GenerationContext): List<HttpFeature> { + val features = super.getHttpFeatures(ctx).toMutableList() + val jsonFeatures = listOf(JSONRequestEncoder()) + features.addAll(jsonFeatures) + return features + } + + private fun renderInputRequestConformanceToCodable(ctx: ProtocolGenerator.GenerationContext, op: OperationShape) { + if (op.input.isEmpty()) { + return + } + val inputShape = ctx.model.expectShape(op.input.get()) + val opIndex = ctx.model.getKnowledge(OperationIndex::class.java) + val inputShapeName = ServiceGenerator.getOperationInputShapeName(ctx.symbolProvider, opIndex, op) + val requestPayloadMembers = getRequestPayloadMembers(ctx, op) + + if (requestPayloadMembers.isNotEmpty()) { + ctx.delegator.useShapeWriter(inputShape) { writer -> + writer.openBlock("extension ${inputShapeName!!.get()}: Codable {", "}") { + writer.openBlock("private enum CodingKeys: String, CodingKey {", "}") { + // TODO:: handle the case when encoding name is different from member name + writer.write("case ${requestPayloadMembers.joinToString(separator = ", ")}") + } + } + writer.write("") + } + } + } + + private fun getRequestPayloadMembers(ctx: ProtocolGenerator.GenerationContext, + op: OperationShape): MutableSet<String> { + val requestPayloadMembers = mutableSetOf<String>() + val bindingIndex = ctx.model.getKnowledge(HttpBindingIndex::class.java) + val requestBindings = bindingIndex.getRequestBindings(op) + val httpPayloadBinding = requestBindings.values.firstOrNull { it.location == HttpBinding.Location.PAYLOAD } + + /* Unbound document members that should be serialized into the document format for the protocol. + */ + val documentMemberBindings = requestBindings.values + .filter { it.location == HttpBinding.Location.DOCUMENT } + .sortedBy { it.memberName } + if (httpPayloadBinding != null) { + val memberName = httpPayloadBinding.member.memberName + requestPayloadMembers.add(memberName) + } else if (documentMemberBindings.isNotEmpty()) { + val documentMemberShapes = documentMemberBindings.map { it.member } + val documentMemberShapesSortedByName: List<MemberShape> = documentMemberShapes.sortedBy { ctx.symbolProvider.toMemberName(it) } + if (documentMemberShapesSortedByName.isNotEmpty()) { + for (member in documentMemberShapesSortedByName) { + val memberName = ctx.symbolProvider.toMemberName(member) + requestPayloadMembers.add(memberName) + } + } + } + return requestPayloadMembers + } +} + + +class JSONRequestEncoder : HttpRequestEncoder("JsonEncoder") {} diff --git a/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration new file mode 100644 index 00000000000..e7059266b7a --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration @@ -0,0 +1 @@ +software.amazon.smithy.aws.swift.codegen.AddProtocols \ No newline at end of file diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt new file mode 100644 index 00000000000..456888f96a8 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.swift.codegen + +import io.kotest.matchers.string.shouldContainOnlyOnce +import org.junit.jupiter.api.Test +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.build.MockManifest +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.swift.codegen.SwiftCodegenPlugin +import software.amazon.smithy.swift.codegen.SwiftDelegator +import software.amazon.smithy.swift.codegen.SwiftSettings +import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator + +class MockRestJsonProtocolGenerator : RestJsonProtocolGenerator() { + override val defaultContentType: String = "application/json" + override val protocol: ShapeId = RestJson1Trait.ID +} + +// NOTE: protocol conformance is mostly handled by the protocol tests suite +class RestJsonProtocolGeneratorTests: TestsBase() { + val model = createModelFromSmithy("http-binding-protocol-generator-test.smithy") + + data class TestContext(val ctx: ProtocolGenerator.GenerationContext, + val manifest: MockManifest, + val generator: MockRestJsonProtocolGenerator) + + private fun newTestContext(): TestContext { + val manifest = MockManifest() + val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(model, "Example") + val serviceShapeIdWithNamespace = "com.test#Example" + val service = model.getShape(ShapeId.from(serviceShapeIdWithNamespace)).get().asServiceShape().get() + val settings = SwiftSettings.from(model, buildDefaultSwiftSettingsObjectNode(serviceShapeIdWithNamespace)) + val delegator = SwiftDelegator(settings, model, manifest, provider) + val generator = MockRestJsonProtocolGenerator() + val ctx = ProtocolGenerator.GenerationContext(settings, model, service, provider, listOf(), "mockHttp", delegator) + return TestContext(ctx, manifest, generator) + } + + val newTestContext = newTestContext() + + init { + newTestContext.generator.generateSerializers(newTestContext.ctx) + newTestContext.ctx.delegator.flushWriters() + } + + @Test + fun `Define coding keys for unbound document payload members`() { + val contents = getModelFileContents("Example","SmokeTestRequest.swift", newTestContext.manifest) + contents.shouldSyntacticSanityCheck() + val expectedContents = + "extension SmokeTestRequest: Codable {\n" + + " private enum CodingKeys: String, CodingKey {\n" + + " case payload1, payload2, payload3\n" + + " }\n" + + "}" + contents.shouldContainOnlyOnce(expectedContents) + } + + @Test + fun `Define coding keys for payload member`() { + val contents = getModelFileContents("Example","ExplicitBlobRequest.swift", newTestContext.manifest) + contents.shouldSyntacticSanityCheck() + val expectedContents = + "extension ExplicitBlobRequest: Codable {\n" + + " private enum CodingKeys: String, CodingKey {\n" + + " case payload1\n" + + " }\n" + + "}" + contents.shouldContainOnlyOnce(expectedContents) + } + + @Test + fun `Defines coding keys for List Input requests`() { + val contents = getModelFileContents("Example","ListInputRequest.swift", newTestContext.manifest) + contents.shouldSyntacticSanityCheck() + val expectedContents = + "extension ListInputRequest: Codable {\n" + + " private enum CodingKeys: String, CodingKey {\n" + + " case intList, nestedIntList, structList\n" + + " }\n" + + "}" + contents.shouldContainOnlyOnce(expectedContents) + } +} diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt new file mode 100644 index 00000000000..1a7e129f0ec --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package software.amazon.smithy.aws.swift.codegen + +import org.junit.jupiter.api.Assertions +import software.amazon.smithy.build.MockManifest +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.ObjectNode +import java.net.URL + +open class TestsBase { + + protected fun createModelFromSmithy(smithyTestResourceName: String): Model { + return Model.assembler() + .addImport(getSmithyResource(smithyTestResourceName)) + .discoverModels() + .assemble() + .unwrap() + } + + protected fun getSmithyResource(smithyTestResourceName: String): URL? { + return TestsBase::class.java.classLoader.getResource("software.amazon.smithy.aws.swift.codegen/$smithyTestResourceName") + } + + protected fun buildDefaultSwiftSettingsObjectNode(serviceShapeId: String, + moduleName: String = "example", + moduleVersion: String = "1.0.0"): ObjectNode { + return Node.objectNodeBuilder() + .withMember("service", Node.from(serviceShapeId)) + .withMember("module", Node.from(moduleName)) + .withMember("moduleVersion", Node.from(moduleVersion)) + .withMember("homepage", Node.from("https://docs.amplify.aws/")) + .withMember("author", Node.from("Amazon Web Services")) + .withMember("gitRepo", Node.from("https://github.com/aws-amplify/amplify-codegen.git")) + .withMember("swiftVersion", Node.from("5.1.0")) + .build() + } + + protected fun getModelFileContents(namespace: String, filename: String, manifest: MockManifest): String { + return manifest.expectFileString("$namespace/models/$filename") + } + + fun String.shouldSyntacticSanityCheck() { + // sanity check the generated code for matching paranthesis + var openBraces = 0 + var closedBraces = 0 + var openParens = 0 + var closedParens = 0 + this.forEach { + when (it) { + '{' -> openBraces++ + '}' -> closedBraces++ + '(' -> openParens++ + ')' -> closedParens++ + } + } + Assertions.assertEquals(openBraces, closedBraces, "unmatched open/closed braces:\n$this") + Assertions.assertEquals(openParens, closedParens, "unmatched open/close parens:\n$this") + } +} \ No newline at end of file diff --git a/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy b/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy new file mode 100644 index 00000000000..821b5b1d650 --- /dev/null +++ b/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy @@ -0,0 +1,198 @@ +$version: "1.0" +namespace com.test + +use aws.protocols#restJson1 + +@restJson1 +service Example { + version: "1.0.0", + operations: [ + SmokeTest, + DuplicateInputTest, + ExplicitString, + ExplicitBlob, + ExplicitBlobStream, + ExplicitStruct, + ListInput, + MapInput, + EnumInput + ] +} + +@http(method: "POST", uri: "/smoketest/{label1}/foo") +operation SmokeTest { + input: SmokeTestRequest, + output: SmokeTestResponse, + errors: [SmokeTestError] +} + +@http(method: "POST", uri: "/smoketest-duplicate/{label1}/foo") +operation DuplicateInputTest { + // uses the same input type as another operation. Ensure that we only generate one instance of the serializer + input: SmokeTestRequest +} + +structure SmokeTestRequest { + @httpHeader("X-Header1") + header1: String, + + @httpHeader("X-Header2") + header2: String, + + @httpQuery("Query1") + query1: String, + + @required + @httpLabel + label1: String, + + payload1: String, + payload2: Integer, + payload3: Nested +} + +structure Nested { + member1: String, + member2: String +} + +structure SmokeTestResponse { + +} + +@error("client") +structure SmokeTestError {} + + +@http(method: "POST", uri: "/explicit/string") +operation ExplicitString { + input: ExplicitStringRequest +} + +structure ExplicitStringRequest { + @httpPayload + payload1: String +} + +@http(method: "POST", uri: "/explicit/blob") +operation ExplicitBlob { + input: ExplicitBlobRequest +} + +structure ExplicitBlobRequest { + @httpPayload + payload1: Blob +} + +@streaming +blob BodyStream + +@http(method: "POST", uri: "/explicit/blobstream") +operation ExplicitBlobStream { + input: ExplicitBlobStreamRequest +} + +structure ExplicitBlobStreamRequest { + @httpPayload + payload1: BodyStream +} + +@http(method: "POST", uri: "/explicit/struct") +operation ExplicitStruct { + input: ExplicitStructRequest +} + +structure Nested4 { + member1: Integer, + // sanity check, member serialization for non top-level (bound to the operation input) aggregate shapes + intList: IntList, + intMap: IntMap +} + +structure Nested3 { + member1: String, + member2: String, + member3: Nested4 +} + +structure Nested2 { + moreNesting: Nested3 +} + +structure ExplicitStructRequest { + @httpPayload + payload1: Nested2 +} + +list IntList { + member: Integer +} + +list StructList { + member: Nested +} + +// A list of lists of integers +list NestedIntList { + member: IntList +} + +@http(method: "POST", uri: "/input/list") +operation ListInput { + input: ListInputRequest +} + +structure ListInputRequest { + intList: IntList, + structList: StructList, + nestedIntList: NestedIntList +} + +map IntMap { + key: String, + value: Integer +} + +map StructMap { + key: String, + value: Nested +} + +@http(method: "POST", uri: "/input/map") +operation MapInput { + input: MapInputRequest +} + +structure MapInputRequest { + intMap: IntMap, + structMap: StructMap +} + + +@http(method: "POST", uri: "/input/enum") +operation EnumInput { + input: EnumInputRequest +} + +@enum([ + { + value: "rawValue1", + name: "Variant1" + }, + { + value: "rawValue2", + name: "Variant2" + } +]) +string MyEnum + +structure NestedEnum { + myEnum: MyEnum +} + +structure EnumInputRequest { + nestedWithEnum: NestedEnum, + + @httpHeader("X-EnumHeader") + enumHeader: MyEnum +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000000..1279f82704d --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +kotlin.code.style=official + +# codegen +smithyVersion=1.0.5 + +# kotlin +kotlinVersion=1.3.72 +kotlin.native.ignoreDisabledTargets=true + + +# testing/utility +# FIXME - junit5 not working +ktlintVersion=0.36.0 +kotestVersion=4.0.5 + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..f3d88b1c2fa Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..1b16c34a71c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000000..2fe81a7d95e --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000000..24467a141f7 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000000..8ac64e57688 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + maven { url = uri("https://dl.bintray.com/kotlin/kotlin-eap") } + maven { url = uri("https://kotlin.bintray.com/kotlinx") } + + gradlePluginPortal() + } +} + +rootProject.name = "aws-sdk-swift" +enableFeaturePreview("GRADLE_METADATA") + + +fun module(path: String) { + val name = path.replace('\\', '/').substringAfterLast('/') + include(name) + project(":$name").projectDir = file(path) +} + + +module("codegen") +module("codegen/smithy-aws-swift-codegen") +module("codegen/protocol-test-codegen")