Skip to content

Commit c66f1a1

Browse files
authored
feat: support bootstrapping services by package name (#633)
1 parent 213e262 commit c66f1a1

File tree

2 files changed

+56
-64
lines changed

2 files changed

+56
-64
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "c6d83bef-e619-497a-9477-8443fd9913f2",
3+
"type": "feature",
4+
"description": "Support bootstrapping services by package name (in addition to by model filename)"
5+
}

codegen/sdk/build.gradle.kts

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -164,81 +164,70 @@ fun transformsForService(service: AwsService): List<String>? {
164164
}
165165

166166
val discoveredServices: List<AwsService> by lazy { discoverServices() }
167+
167168
// The root namespace prefix for SDKs
168169
val sdkPackageNamePrefix = "aws.sdk.kotlin.services."
169170

170-
/**
171-
* Returns an AwsService model for every JSON file found in directory defined by property `modelsDirProp`
172-
* @param applyFilters Flag indicating if models should be filtered to respect the `aws.services` and `aws.protocol`
173-
* membership tests
174-
*/
175-
fun discoverServices(applyFilters: Boolean = true): List<AwsService> {
176-
println("discover services called")
177-
val modelsDir: String by project
178-
val serviceMembership = parseMembership(getProperty("aws.services"))
179-
val protocolMembership = parseMembership(getProperty("aws.protocols"))
180-
181-
return fileTree(project.file(modelsDir))
182-
.filter { file ->
183-
if (!applyFilters) return@filter true
184-
val svcName = file.name.split(".").first()
185-
val include = serviceMembership.isMember(svcName)
186-
187-
if (!include) {
188-
logger.info("skipping ${file.absolutePath}, $svcName not a member of $serviceMembership")
189-
}
190-
191-
val isDisabled = svcName in disabledServices
192-
if (include && isDisabled) {
193-
logger.warn("skipping $svcName because it is explicitly disabled")
194-
}
195-
196-
include && !isDisabled
171+
val sdkVersion: String by project
172+
173+
val serviceMembership: Membership by lazy { parseMembership(getProperty("aws.services")) }
174+
val protocolMembership: Membership by lazy { parseMembership(getProperty("aws.protocols")) }
175+
176+
fun fileToService(applyFilters: Boolean): (File) -> AwsService? = { file: File ->
177+
val filename = file.nameWithoutExtension
178+
val model = Model.assembler().addImport(file.absolutePath).assemble().result.get()
179+
val services: List<ServiceShape> = model.shapes(ServiceShape::class.java).sorted().toList()
180+
val service = services.singleOrNull() ?: error("Expected one service per aws model, but found ${services.size} in ${file.absolutePath}: ${services.map { it.id }}")
181+
val protocol = service.protocol()
182+
val serviceTrait = service.getTrait(software.amazon.smithy.aws.traits.ServiceTrait::class.java).orNull()
183+
?: error { "Expected aws.api#service trait attached to model ${file.absolutePath}" }
184+
val sdkId = serviceTrait.sdkId
185+
val packageName = sdkId.replace(" ", "")
186+
.replace("-", "")
187+
.toLowerCase()
188+
.kotlinNamespace()
189+
190+
when {
191+
applyFilters && !serviceMembership.isMember(filename, packageName) -> {
192+
logger.info("skipping ${file.absolutePath}, $filename/$packageName not a member of $serviceMembership")
193+
null
197194
}
198-
.map { file ->
199-
val model = Model.assembler().addImport(file.absolutePath).assemble().result.get()
200-
val services: List<ServiceShape> = model.shapes(ServiceShape::class.java).sorted().toList()
201-
require(services.size == 1) { "Expected one service per aws model, but found ${services.size} in ${file.absolutePath}: ${services.map { it.id }}" }
202-
val service = services.first()
203-
file to service
204-
}
205-
.filter { (file, service) ->
206-
if (!applyFilters) return@filter true
207-
val protocol = service.protocol()
208-
val include = protocolMembership.isMember(protocol)
209195

210-
if (!include) {
211-
logger.info("skipping ${file.absolutePath}, $protocol not a member of $protocolMembership")
212-
}
213-
include
196+
applyFilters && !protocolMembership.isMember(protocol) -> {
197+
logger.info("skipping ${file.absolutePath}, $protocol not a member of $protocolMembership")
198+
null
214199
}
215-
.map { (file, service) ->
216-
val serviceTrait = service.getTrait(software.amazon.smithy.aws.traits.ServiceTrait::class.java).orNull()
217-
?: error { "Expected aws.api#service trait attached to model ${file.absolutePath}" }
218-
val name = file.nameWithoutExtension
219200

220-
// derive a package namespace based on sdkId which should end up unique across AWS services
221-
val packageName = serviceTrait.sdkId.replace(" ", "")
222-
.replace("-", "")
223-
.toLowerCase()
224-
.kotlinNamespace()
225-
226-
val description = service.getTrait(software.amazon.smithy.model.traits.TitleTrait::class.java).map { it.value }.orNull()
201+
applyFilters && filename in disabledServices -> {
202+
logger.warn("skipping ${file.absolutePath}, it is explicitly disabled")
203+
null
204+
}
227205

206+
else -> {
228207
logger.info("discovered service: ${serviceTrait.sdkId}")
229-
230-
val sdkVersion: String by project
231208
AwsService(
232209
serviceShapeId = service.id.toString(),
233210
packageName = "$sdkPackageNamePrefix$packageName",
234211
packageVersion = sdkVersion,
235212
modelFile = file,
236-
projectionName = name,
237-
sdkId = serviceTrait.sdkId,
213+
projectionName = filename,
214+
sdkId = sdkId,
238215
version = service.version,
239-
description = description
216+
description = description,
240217
)
241218
}
219+
}
220+
}
221+
222+
/**
223+
* Returns an AwsService model for every JSON file found in directory defined by property `modelsDirProp`
224+
* @param applyFilters Flag indicating if models should be filtered to respect the `aws.services` and `aws.protocol`
225+
* membership tests
226+
*/
227+
fun discoverServices(applyFilters: Boolean = true): List<AwsService> {
228+
println("discover services called")
229+
val modelsDir: String by project
230+
return fileTree(project.file(modelsDir)).mapNotNull(fileToService(applyFilters))
242231
}
243232

244233
// Returns the trait name of the protocol of the service
@@ -254,12 +243,10 @@ fun ServiceShape.protocol(): String =
254243

255244
// Class and functions for service and protocol membership for SDK generation
256245
data class Membership(val inclusions: Set<String> = emptySet(), val exclusions: Set<String> = emptySet())
257-
fun Membership.isMember(member: String): Boolean = when {
258-
exclusions.contains(member) -> false
259-
inclusions.contains(member) -> true
260-
inclusions.isEmpty() -> true
261-
else -> false
262-
}
246+
247+
fun Membership.isMember(vararg memberNames: String): Boolean =
248+
memberNames.none(exclusions::contains) && (inclusions.isEmpty() || memberNames.any(inclusions::contains))
249+
263250
fun parseMembership(rawList: String?): Membership {
264251
if (rawList == null) return Membership()
265252

0 commit comments

Comments
 (0)