Skip to content

Commit e733c37

Browse files
committed
chore: generate a top-level OVERVIEW.md doc for each service module
1 parent 4f3b3fe commit e733c37

File tree

6 files changed

+50
-165
lines changed

6 files changed

+50
-165
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ local.properties
1717
services/*/generated-src
1818
services/*/build.gradle.kts
1919
services/*/API.md
20+
services/*/OVERVIEW.md
2021
.kotest/
2122
.kotlin/
2223
*.klib

build.gradle.kts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,18 @@ allprojects {
6666
}
6767

6868
tasks.withType<org.jetbrains.dokka.gradle.DokkaTaskPartial>().configureEach {
69-
// each module can include their own top-level module documentation
70-
// see https://kotlinlang.org/docs/dokka-module-and-package-docs.html
71-
if (project.file("API.md").exists()) {
72-
dokkaSourceSets.configureEach {
73-
includes.from(project.file("API.md"))
74-
}
75-
}
76-
7769
dokkaSourceSets.configureEach {
7870
samples.from(project.file("samples").path, project.file("generated-src/samples").path)
71+
72+
// Each module can include their own top-level module documentation in one or more included Markdown files,
73+
// each of which must begin with `# Module <module-name>` where <module-name> is the literal name of the
74+
// Gradle module. See https://kotlinlang.org/docs/dokka-module-and-package-docs.html for more details.
75+
val includeFiles = setOf(
76+
"OVERVIEW.md", // Auto-generated by ModuleDocumentationIntegration
77+
"DOCS.md", // Hand-written docs explaining a module in greater detail
78+
"API.md", // Auto-generated by `kat` tool
79+
).mapNotNull { project.file(it).takeIf { it.exists() } }
80+
includes.from(includeFiles)
7981
}
8082

8183
val smithyKotlinPackageListUrl: String? by project
Lines changed: 13 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,31 @@
11
package aws.sdk.kotlin.codegen
22

3-
import software.amazon.smithy.kotlin.codegen.KotlinSettings
3+
import software.amazon.smithy.aws.traits.ServiceTrait
44
import software.amazon.smithy.kotlin.codegen.core.CodegenContext
55
import software.amazon.smithy.kotlin.codegen.core.KotlinDelegator
66
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
77
import software.amazon.smithy.kotlin.codegen.model.expectShape
88
import software.amazon.smithy.kotlin.codegen.model.getTrait
9-
import software.amazon.smithy.model.Model
109
import software.amazon.smithy.model.shapes.ServiceShape
1110
import software.amazon.smithy.model.traits.TitleTrait
12-
import java.io.File
1311

1412
/**
15-
* Maps a service's SDK ID to its code examples
13+
* Generates an `OVERVIEW.md` file that will provide a brief intro for each service module in API reference docs.
1614
*/
17-
private val CODE_EXAMPLES_SERVICES_MAP = mapOf(
18-
"API Gateway" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_api-gateway_code_examples.html",
19-
"Auto Scaling" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_auto-scaling_code_examples.html",
20-
"Bedrock" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_bedrock_code_examples.html",
21-
"CloudWatch" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_cloudwatch_code_examples.html",
22-
"Comprehend" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_comprehend_code_examples.html",
23-
"DynamoDB" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_dynamodb_code_examples.html",
24-
"EC2" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_ec2_code_examples.html",
25-
"ECR" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_ecr_code_examples.html",
26-
"OpenSearch" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_opensearch_code_examples.html",
27-
"EventBridge" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_eventbridge_code_examples.html",
28-
"Glue" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_glue_code_examples.html",
29-
"IAM" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_iam_code_examples.html",
30-
"IoT" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_iot_code_examples.html ",
31-
"Keyspaces" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_keyspaces_code_examples.html",
32-
"KMS" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_kms_code_examples.html",
33-
"Lambda" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_lambda_code_examples.html",
34-
"MediaConvert" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_mediaconvert_code_examples.html",
35-
"Pinpoint" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_pinpoint_code_examples.html",
36-
"RDS" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_rds_code_examples.html",
37-
"Redshift" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_redshift_code_examples.html",
38-
"Rekognition" to "https://docs.aws.amazon.com/code-library/latest/ug/kotlin_1_rekognition_code_examples.html",
39-
)
40-
41-
/**
42-
* Generates an `API.md` file that will be used as module documentation in our API ref docs.
43-
* Some services have code example documentation we need to generate. Others have handwritten documentation.
44-
* The integration renders both into the `API.md` file.
45-
*
46-
* See: https://kotlinlang.org/docs/dokka-module-and-package-docs.html
47-
*
48-
* See: https://github.com/awslabs/aws-sdk-kotlin/blob/0581f5c5eeaa14dcd8af4ea0dfc088b1057f5ba5/build.gradle.kts#L68-L75
49-
*/
50-
class ModuleDocumentationIntegration(
51-
private val codeExamples: Map<String, String> = CODE_EXAMPLES_SERVICES_MAP,
52-
) : KotlinIntegration {
53-
override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
54-
codeExamples.keys.contains(
55-
model
56-
.expectShape<ServiceShape>(settings.service)
57-
.sdkId,
58-
) ||
59-
handWrittenDocsFile(settings).exists()
60-
15+
class ModuleDocumentationIntegration : KotlinIntegration {
6116
override fun writeAdditionalFiles(ctx: CodegenContext, delegator: KotlinDelegator) {
62-
delegator.fileManifest.writeFile(
63-
"API.md",
64-
generateModuleDocumentation(ctx),
65-
)
66-
}
67-
68-
internal fun generateModuleDocumentation(
69-
ctx: CodegenContext,
70-
) = buildString {
71-
val handWrittenDocsFile = handWrittenDocsFile(ctx.settings)
72-
if (handWrittenDocsFile.exists()) {
73-
append(
74-
handWrittenDocsFile.readText(),
75-
)
17+
val overview = buildString {
18+
val moduleName = ctx.settings.pkg.name.split(".").last()
19+
val service = ctx.model.expectShape<ServiceShape>(ctx.settings.service)
20+
val title = service.getTrait<TitleTrait>()?.value
21+
?: service.getTrait<ServiceTrait>()?.cloudFormationName
22+
?: moduleName
23+
24+
appendLine("# Module $moduleName")
7625
appendLine()
26+
appendLine("This module contains the Kotlin SDK client for **$title**.")
7727
}
78-
if (codeExamples.keys.contains(ctx.settings.sdkId)) {
79-
if (!handWrittenDocsFile.exists()) {
80-
append(
81-
boilerPlate(ctx),
82-
)
83-
}
84-
append(
85-
codeExamplesDocs(ctx),
86-
)
87-
}
88-
}
89-
90-
private fun boilerPlate(ctx: CodegenContext) = buildString {
91-
// Title must be "Module" followed by the exact module name or dokka won't render it
92-
appendLine("# Module ${ctx.settings.pkg.name.split(".").last()}")
93-
appendLine()
94-
ctx
95-
.model
96-
.expectShape<ServiceShape>(ctx.settings.service)
97-
.getTrait<TitleTrait>()
98-
?.value
99-
?.let {
100-
appendLine(it)
101-
appendLine()
102-
}
103-
}
104-
105-
private fun codeExamplesDocs(ctx: CodegenContext) = buildString {
106-
val sdkId = ctx.settings.sdkId
107-
val codeExampleLink = codeExamples[sdkId]
108-
val title = ctx
109-
.model
110-
.expectShape<ServiceShape>(ctx.settings.service)
111-
.getTrait<TitleTrait>()
112-
?.value
11328

114-
appendLine("## Code Examples")
115-
appendLine("Explore code examples for ${title ?: sdkId} in the <a href=\"$codeExampleLink\">AWS code example library</a>.")
116-
appendLine()
29+
delegator.fileManifest.writeFile("OVERVIEW.md", overview)
11730
}
11831
}
119-
120-
private fun handWrittenDocsFile(settings: KotlinSettings): File {
121-
val sdkRootDir = System.getProperty("user.dir")
122-
val serviceDir = "$sdkRootDir/services/${settings.pkg.name.split(".").last()}"
123-
124-
return File("$serviceDir/DOCS.md")
125-
}
Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package aws.sdk.kotlin.codegen
22

3+
import software.amazon.smithy.build.MockManifest
34
import software.amazon.smithy.kotlin.codegen.test.newTestContext
4-
import software.amazon.smithy.kotlin.codegen.test.shouldContainOnlyOnceWithDiff
5-
import software.amazon.smithy.kotlin.codegen.test.toGenerationContext
5+
import software.amazon.smithy.kotlin.codegen.test.toCodegenContext
66
import software.amazon.smithy.kotlin.codegen.test.toSmithyModel
77
import kotlin.test.Test
8-
import kotlin.test.assertFalse
8+
import kotlin.test.assertEquals
99
import kotlin.test.assertTrue
1010

1111
private val model = """
@@ -28,40 +28,21 @@ val ctx = model.newTestContext("Test")
2828
class ModuleDocumentationIntegrationTest {
2929
@Test
3030
fun integrationIsAppliedCorrectly() {
31-
assertFalse(
32-
ModuleDocumentationIntegration().enabledForService(model, ctx.generationCtx.settings),
33-
)
34-
assertTrue(
35-
ModuleDocumentationIntegration(
36-
codeExamples = mapOf("Test" to "https://example.com"),
37-
).enabledForService(model, ctx.generationCtx.settings),
38-
)
39-
}
31+
val integration = ModuleDocumentationIntegration()
32+
assertTrue(integration.enabledForService(model, ctx.generationCtx.settings))
4033

41-
@Test
42-
fun rendersBoilerplate() =
43-
ModuleDocumentationIntegration(
44-
codeExamples = mapOf("Test" to "https://example.com"),
45-
)
46-
.generateModuleDocumentation(ctx.toGenerationContext())
47-
.shouldContainOnlyOnceWithDiff(
48-
"""
49-
# Module test
50-
51-
Test Service
52-
""".trimIndent(),
53-
)
34+
integration.writeAdditionalFiles(ctx.toCodegenContext(), ctx.generationCtx.delegator)
35+
ctx.generationCtx.delegator.flushWriters()
36+
val testManifest = ctx.generationCtx.delegator.fileManifest as MockManifest
5437

55-
@Test
56-
fun rendersCodeExampleDocs() =
57-
ModuleDocumentationIntegration(
58-
codeExamples = mapOf("Test" to "https://example.com"),
59-
)
60-
.generateModuleDocumentation(ctx.toGenerationContext())
61-
.shouldContainOnlyOnceWithDiff(
62-
"""
63-
## Code Examples
64-
Explore code examples for Test Service in the <a href="https://example.com">AWS code example library</a>
65-
""".trimIndent(),
66-
)
38+
val actual = testManifest.expectFileString("OVERVIEW.md")
39+
val expected = """
40+
# Module test
41+
42+
This module contains the Kotlin SDK client for **Test Service**.
43+
44+
""".trimIndent()
45+
46+
assertEquals(expected, actual)
47+
}
6748
}

codegen/sdk/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ val stageSdks = tasks.register("stageSdks") {
181181
into(it.destinationDir)
182182
}
183183
copy {
184-
from("$projectionOutputDir/API.md")
184+
from("$projectionOutputDir/OVERVIEW.md")
185185
into(it.destinationDir)
186186
}
187187
}

services/s3/DOCS.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
# Module s3
22

3-
Amazon Simple Storage Service
4-
53
## Binary Data
64

7-
Binary data (streams) are represented as an [aws.smithy.kotlin.runtime.content.ByteStream].
8-
9-
To supply a `ByteStream` there are several convenience functions including:
5+
Binary data (streams) are represented as a [`ByteStream`][aws.smithy.kotlin.runtime.content.ByteStream]. To supply a
6+
`ByteStream` there are several convenience functions including:
107

118
```kt
129
val req = PutObjectRequest {
@@ -30,15 +27,12 @@ s3.getObject(req) { resp -> {
3027
}
3128
```
3229

33-
See [aws.sdk.kotlin.services.s3.model.GetObjectResponse]
30+
See [`GetObjectResponse`][aws.sdk.kotlin.services.s3.model.GetObjectResponse] for more details.
3431

3532
## Streaming Responses
3633

37-
Streaming responses are scoped to a `block`. Instead of returning the response directly, you must pass a lambda which is given access to the response (and the underlying stream).
38-
The result of the call is whatever the lambda returns.
39-
40-
41-
See [aws.sdk.kotlin.services.s3.S3Client.getObject]
34+
Streaming responses are scoped to a `block`. Instead of returning the response directly, you must pass a lambda which is
35+
given access to the response (and the underlying stream). The result of the call is whatever the lambda returns.
4236

4337
```kt
4438
val s3 = S3Client { ... }
@@ -57,5 +51,6 @@ val contentSize = s3.getObject(req) { resp ->
5751
println("wrote $contentSize bytes to $path")
5852
```
5953

54+
This scoped response simplifies lifetime management for both the caller and the runtime.
6055

61-
This scoped response simplifies lifetime management for both the caller and the runtime.
56+
See [`getObject`][aws.sdk.kotlin.services.s3.S3Client.getObject] for more details.

0 commit comments

Comments
 (0)