-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
2,699 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
build-support/src/main/kotlin/aws/sdk/kotlin/gradle/sdk/PackageManifest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package aws.sdk.kotlin.gradle.sdk | ||
|
||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.Json | ||
import kotlinx.serialization.json.decodeFromStream | ||
import java.io.File | ||
|
||
/** | ||
* Manifest containing additional metadata about services. | ||
*/ | ||
@OptIn(ExperimentalSerializationApi::class) | ||
@Serializable | ||
data class PackageManifest( | ||
val packages: List<PackageMetadata>, | ||
) { | ||
|
||
val bySdkId: Map<String, PackageMetadata> = packages.associateBy(PackageMetadata::sdkId) | ||
companion object { | ||
fun fromFile(file: File): PackageManifest = | ||
file.inputStream().use { | ||
Json.decodeFromStream<PackageManifest>(it) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Validate the package manifest for errors throwing an exception if any exist. | ||
*/ | ||
fun PackageManifest.validate() { | ||
val distinct = mutableMapOf<String, PackageMetadata>() | ||
val errors = mutableListOf<String>() | ||
packages.forEach { | ||
val existing = distinct[it.sdkId] | ||
if (existing != null) { | ||
errors.add("multiple packages with same sdkId `${it.sdkId}`: first: $existing; second: $it") | ||
} | ||
distinct[it.sdkId] = it | ||
} | ||
|
||
check(errors.isEmpty()) { errors.joinToString(separator = "\n") } | ||
} | ||
|
||
/** | ||
* Per/package metadata stored with the repository. | ||
* | ||
* @param sdkId the unique SDK ID from the model this metadata applies to | ||
* @param namespace the package namespace to use as the root namespace when generating code for this package | ||
* @param artifactName the Maven artifact name (i.e. the 'A' in 'GAV' coordinates) | ||
* @param brazilName the internal Brazil package name for this package | ||
*/ | ||
@Serializable | ||
data class PackageMetadata( | ||
public val sdkId: String, | ||
public val namespace: String, | ||
public val artifactName: String, | ||
public val brazilName: String, | ||
) { | ||
companion object { | ||
|
||
/** | ||
* Create a new [PackageMetadata] from inferring values using the given sdkId | ||
*/ | ||
fun from(sdkId: String): PackageMetadata = | ||
PackageMetadata( | ||
sdkId, | ||
packageNamespaceForService(sdkId), | ||
sdkIdToArtifactName(sdkId), | ||
sdkIdToBrazilName(sdkId), | ||
) | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
build-support/src/main/kotlin/aws/sdk/kotlin/gradle/sdk/tasks/UpdatePackageManifest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package aws.sdk.kotlin.gradle.sdk.tasks | ||
|
||
import aws.sdk.kotlin.gradle.sdk.PackageManifest | ||
import aws.sdk.kotlin.gradle.sdk.PackageMetadata | ||
import aws.sdk.kotlin.gradle.sdk.orNull | ||
import aws.sdk.kotlin.gradle.sdk.validate | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.encodeToString | ||
import kotlinx.serialization.json.Json | ||
import org.gradle.api.DefaultTask | ||
import org.gradle.api.file.DirectoryProperty | ||
import org.gradle.api.file.RegularFileProperty | ||
import org.gradle.api.provider.Property | ||
import org.gradle.api.tasks.* | ||
import org.gradle.api.tasks.options.Option | ||
import software.amazon.smithy.aws.traits.ServiceTrait | ||
import software.amazon.smithy.model.Model | ||
import software.amazon.smithy.model.shapes.ServiceShape | ||
import kotlin.streams.toList | ||
|
||
/** | ||
* Task to update the package manifest which is used by the bootstrap process to generate service clients. | ||
* New services are required to be scaffolded | ||
*/ | ||
abstract class UpdatePackageManifest : DefaultTask() { | ||
|
||
@get:Option(option = "model", description = "the path to a single model file to scaffold") | ||
@get:Optional | ||
@get:InputFile | ||
public abstract val modelFile: RegularFileProperty | ||
|
||
@get:Optional | ||
@get:Option(option = "model-dir", description = "the path to a directory of model files to scaffold") | ||
@get:InputDirectory | ||
public abstract val modelDir: DirectoryProperty | ||
|
||
@get:Optional | ||
@get:Option( | ||
option = "discover", | ||
description = "Flag to discover and process only new packages not currently in the manifest. Only applicable when used in conjunction with `model-dir`", | ||
) | ||
@get:Input | ||
public abstract val discover: Property<Boolean> | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
@TaskAction | ||
fun updatePackageManifest() { | ||
check(modelFile.isPresent != modelDir.isPresent) { "Exactly one of `model` or `model-dir` must be set" } | ||
|
||
val manifestFile = project.file("packages.json") | ||
|
||
val manifest = if (manifestFile.exists()) { | ||
val manifest = PackageManifest.fromFile(manifestFile) | ||
manifest.validate() | ||
manifest | ||
} else { | ||
PackageManifest(emptyList()) | ||
} | ||
|
||
val model = Model.assembler() | ||
.discoverModels() | ||
.apply { | ||
val import = if (modelFile.isPresent) modelFile else modelDir | ||
addImport(import.get().asFile.absolutePath) | ||
} | ||
.assemble() | ||
.result | ||
.get() | ||
|
||
val discoveredPackages = model | ||
.shapes(ServiceShape::class.java) | ||
.toList() | ||
.mapNotNull { it.getTrait(ServiceTrait::class.java).orNull()?.sdkId } | ||
.map { PackageMetadata.from(it) } | ||
|
||
val newPackages = validatedPackages(manifest, discoveredPackages) | ||
|
||
if (newPackages.isEmpty()) { | ||
logger.lifecycle("no new packages to scaffold") | ||
return | ||
} | ||
|
||
logger.lifecycle("scaffolding ${newPackages.size} new service packages") | ||
|
||
val updatedPackages = manifest.packages + newPackages | ||
val updatedManifest = manifest.copy(packages = updatedPackages.sortedBy { it.sdkId }) | ||
|
||
val json = Json { prettyPrint = true } | ||
val contents = json.encodeToString(updatedManifest) | ||
manifestFile.writeText(contents) | ||
} | ||
|
||
private fun validatedPackages(manifest: PackageManifest, discovered: List<PackageMetadata>): List<PackageMetadata> = | ||
if (modelDir.isPresent && discover.orNull == true) { | ||
val bySdkId = manifest.packages.associateBy(PackageMetadata::sdkId) | ||
discovered.filter { it.sdkId !in bySdkId } | ||
} else { | ||
discovered.forEach { pkg -> | ||
val existing = manifest.packages.find { it.sdkId == pkg.sdkId } | ||
check(existing == null) { "found existing package in manifest for sdkId `${pkg.sdkId}`: $existing" } | ||
} | ||
discovered | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
build-support/src/test/kotlin/aws/sdk/kotlin/gradle/sdk/PackageManifestTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package aws.sdk.kotlin.gradle.sdk | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertContains | ||
import kotlin.test.assertFailsWith | ||
|
||
class PackageManifestTest { | ||
@Test | ||
fun testValidate() { | ||
val manifest = PackageManifest( | ||
listOf( | ||
PackageMetadata("Package 1", "aws.sdk.kotlin.services.package1", "package1", "AwsSdkKotlinPackage1"), | ||
PackageMetadata("Package 2", "aws.sdk.kotlin.services.package2", "package2", "AwsSdkKotlinPackage2"), | ||
), | ||
) | ||
|
||
manifest.validate() | ||
|
||
val badManifest = manifest.copy( | ||
manifest.packages + listOf( | ||
PackageMetadata("Package 2", "aws.sdk.kotlin.services.package2", "package2", "AwsSdkKotlinPackage2"), | ||
), | ||
) | ||
|
||
val ex = assertFailsWith<IllegalStateException> { badManifest.validate() } | ||
|
||
assertContains(ex.message!!, "multiple packages with same sdkId `Package 2`") | ||
} | ||
} |
Oops, something went wrong.