Skip to content

Commit 3d2c167

Browse files
authored
Add code example links to individual method documentation in API refs guide (#6626)
* Add code example links to individual method documentation * Address PR feedback * Fixing build failure * Add code examples to package summary pages * Fixing build failure * Minor changes * Address PR comments * Additional changes * Fix checkstyles * Review comments addressed * Minor changes * Fix sonarqube issue
1 parent dcdf01d commit 3d2c167

File tree

12 files changed

+739
-9
lines changed

12 files changed

+739
-9
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Adds links to AWS Code Library examples in package summary documentation and to individual methods documentation in Java SDK API Reference guide."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/docs/OperationDocProvider.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515

1616
package software.amazon.awssdk.codegen.docs;
1717

18+
import static software.amazon.awssdk.codegen.internal.Constant.EXAMPLE_META_PATH;
1819
import static software.amazon.awssdk.codegen.internal.DocumentationUtils.createLinkToServiceDocumentation;
1920
import static software.amazon.awssdk.codegen.internal.DocumentationUtils.stripHtmlTags;
2021

2122
import com.squareup.javapoet.ClassName;
2223
import java.util.Collections;
2324
import java.util.List;
25+
import java.util.Optional;
2426
import java.util.stream.Collectors;
27+
import software.amazon.awssdk.codegen.internal.ExampleMetadataProvider;
2528
import software.amazon.awssdk.codegen.model.intermediate.DocumentationModel;
2629
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2730
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
@@ -51,6 +54,9 @@ abstract class OperationDocProvider {
5154
"this method will throw an exception. If the file is not writable by the current user then " +
5255
"an exception will be thrown. ";
5356

57+
private static final ExampleMetadataProvider EXAMPLE_PROVIDER =
58+
new ExampleMetadataProvider(EXAMPLE_META_PATH);
59+
5460
protected final IntermediateModel model;
5561
protected final OperationModel opModel;
5662
protected final DocConfiguration config;
@@ -86,6 +92,10 @@ String getDocs() {
8692
if (!crosslink.isEmpty()) {
8793
docBuilder.see(crosslink);
8894
}
95+
96+
Optional<String> codeExampleLink = EXAMPLE_PROVIDER
97+
.createLinkToCodeExample(model.getMetadata(), opModel.getOperationName());
98+
codeExampleLink.ifPresent(docBuilder::see);
8999
return docBuilder.build().replace("$", "&#36");
90100
}
91101

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/PackageInfoGeneratorTasks.java

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,42 @@
1515

1616
package software.amazon.awssdk.codegen.emitters.tasks;
1717

18+
import static software.amazon.awssdk.codegen.internal.Constant.EXAMPLE_META_PATH;
19+
1820
import java.util.Collections;
21+
import java.util.LinkedHashMap;
1922
import java.util.List;
23+
import java.util.Map;
24+
import java.util.stream.Collectors;
2025
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
2126
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
2227
import software.amazon.awssdk.codegen.emitters.SimpleGeneratorTask;
28+
import software.amazon.awssdk.codegen.internal.ExampleMetadataProvider;
2329
import software.amazon.awssdk.codegen.model.intermediate.Metadata;
30+
import software.amazon.awssdk.utils.CollectionUtils;
2431

2532
/**
2633
* Emits the package-info.java for the base service package. Includes the service
27-
* level documentation.
34+
* level documentation and code examples for that service organized by category.
2835
*/
2936
public final class PackageInfoGeneratorTasks extends BaseGeneratorTasks {
3037

38+
/**
39+
* Mapping from internal category names to user-friendly display names.
40+
* This defines the preferred order and display format for code example categories.
41+
*/
42+
private static final Map<String, String> CATEGORY_DISPLAY_MAPPING;
43+
44+
static {
45+
Map<String, String> mapping = new LinkedHashMap<>();
46+
mapping.put("Hello", "Getting Started");
47+
mapping.put("Basics", "Basics");
48+
mapping.put("Api", "API Actions");
49+
mapping.put("Scenarios", "Scenarios");
50+
mapping.put("Serverless examples", "Serverless Examples");
51+
CATEGORY_DISPLAY_MAPPING = Collections.unmodifiableMap(mapping);
52+
}
53+
3154
private final String baseDirectory;
3255

3356
PackageInfoGeneratorTasks(GeneratorTaskParams dependencies) {
@@ -38,17 +61,150 @@ public final class PackageInfoGeneratorTasks extends BaseGeneratorTasks {
3861
@Override
3962
protected List<GeneratorTask> createTasks() throws Exception {
4063
Metadata metadata = model.getMetadata();
41-
String packageInfoContents =
42-
String.format("/**%n"
43-
+ " * %s%n"
44-
+ "*/%n"
45-
+ "package %s;",
46-
metadata.getDocumentation(),
47-
metadata.getFullClientPackageName());
64+
String packageInfoContents = buildPackageInfoContent(metadata, EXAMPLE_META_PATH);
65+
4866
return Collections.singletonList(new SimpleGeneratorTask(baseDirectory,
4967
"package-info.java",
5068
model.getFileHeader(),
5169
() -> packageInfoContents));
5270
}
5371

72+
/**
73+
* Builds the complete package-info.java content including Javadoc and package declaration.
74+
*
75+
* @param metadata the service metadata containing documentation and package information
76+
* @param exampleMetaPath the path to the example metadata JSON file
77+
* @return the complete package-info.java file content
78+
*/
79+
String buildPackageInfoContent(Metadata metadata, String exampleMetaPath) {
80+
String baseDocumentation = metadata.getDocumentation();
81+
String codeExamples = getCodeExamplesWithPath(metadata, exampleMetaPath);
82+
String javadocContent = buildJavadocContent(baseDocumentation, codeExamples);
83+
84+
return javadocContent + System.lineSeparator() + "package " + metadata.getFullClientPackageName() + ";";
85+
}
86+
87+
/**
88+
* Builds the Javadoc comment content for the package-info.java file.
89+
*
90+
* @param baseDocumentation the base service documentation
91+
* @param codeExamples the formatted code examples content, or empty string if none
92+
* @return the complete Javadoc comment including opening and closing markers
93+
*/
94+
private String buildJavadocContent(String baseDocumentation, String codeExamples) {
95+
StringBuilder javadoc = new StringBuilder();
96+
javadoc.append("/**").append(System.lineSeparator());
97+
javadoc.append(" * ").append(baseDocumentation).append(System.lineSeparator());
98+
99+
if (!codeExamples.isEmpty()) {
100+
javadoc.append(" *").append(System.lineSeparator());
101+
javadoc.append(" * ").append(codeExamples).append(System.lineSeparator());
102+
}
103+
104+
javadoc.append(" */");
105+
return javadoc.toString();
106+
}
107+
108+
/**
109+
* Gets code examples using a custom example metadata path.
110+
*/
111+
private String getCodeExamplesWithPath(Metadata metadata, String exampleMetaPath) {
112+
ExampleMetadataProvider exampleProvider = new ExampleMetadataProvider(exampleMetaPath);
113+
List<ExampleMetadataProvider.ExampleData> examples = exampleProvider.getServiceCodeExamples(metadata);
114+
115+
if (examples.isEmpty()) {
116+
return "";
117+
}
118+
119+
return generateCodeExamplesJavadoc(examples);
120+
}
121+
122+
private String generateCodeExamplesJavadoc(List<ExampleMetadataProvider.ExampleData> examples) {
123+
Map<String, List<ExampleMetadataProvider.ExampleData>> categorizedExamples =
124+
examples.stream().collect(Collectors.groupingBy(ExampleMetadataProvider.ExampleData::getCategory,
125+
LinkedHashMap::new,
126+
Collectors.toList()));
127+
128+
StringBuilder javadoc = new StringBuilder();
129+
javadoc.append("<h2>Code Examples</h2>").append(System.lineSeparator());
130+
javadoc.append("<p>For code examples demonstrating how to use this service with the AWS SDK for Java v2, see:</p>")
131+
.append(System.lineSeparator());
132+
133+
appendPredefinedCategories(javadoc, categorizedExamples, CATEGORY_DISPLAY_MAPPING);
134+
appendRemainingCategories(javadoc, categorizedExamples, CATEGORY_DISPLAY_MAPPING);
135+
136+
return formatAsJavadocLines(javadoc.toString());
137+
}
138+
139+
/**
140+
* Formats HTML content as properly indented Javadoc comment lines.
141+
* Each non-empty line gets prefixed with appropriate Javadoc comment formatting.
142+
*
143+
* @param htmlContent the HTML content to format for Javadoc
144+
* @return formatted string ready for inclusion in Javadoc comments
145+
*/
146+
private String formatAsJavadocLines(String htmlContent) {
147+
StringBuilder result = new StringBuilder();
148+
String[] lines = htmlContent.split(System.lineSeparator());
149+
150+
for (int i = 0; i < lines.length; i++) {
151+
String line = lines[i];
152+
if (!line.trim().isEmpty()) {
153+
result.append(line);
154+
if (i < lines.length - 1) {
155+
result.append(System.lineSeparator()).append(" * ");
156+
}
157+
}
158+
}
159+
return result.toString();
160+
}
161+
162+
/**
163+
* Appends predefined categories to the Javadoc in the preferred order.
164+
* Only includes categories that exist in the categorized examples and have content.
165+
*/
166+
private void appendPredefinedCategories(StringBuilder javadoc,
167+
Map<String, List<ExampleMetadataProvider.ExampleData>> categorizedExamples,
168+
Map<String, String> categoryMapping) {
169+
categoryMapping.forEach((category, displayName) ->
170+
appendCategoryIfExists(javadoc, categorizedExamples, category, displayName));
171+
}
172+
173+
/**
174+
* Appends any remaining categories that weren't in the predefined mapping.
175+
*/
176+
private void appendRemainingCategories(StringBuilder javadoc,
177+
Map<String, List<ExampleMetadataProvider.ExampleData>> categorizedExamples,
178+
Map<String, String> categoryMapping) {
179+
categorizedExamples.entrySet().stream()
180+
.filter(entry -> !categoryMapping.containsKey(entry.getKey()))
181+
.forEach(entry -> appendCategoryIfExists(javadoc, categorizedExamples, entry.getKey(),
182+
entry.getKey()));
183+
}
184+
185+
/**
186+
* Appends a category section if examples exist for the given category.
187+
*/
188+
private void appendCategoryIfExists(StringBuilder javadoc,
189+
Map<String, List<ExampleMetadataProvider.ExampleData>> categorizedExamples,
190+
String category,
191+
String displayName) {
192+
List<ExampleMetadataProvider.ExampleData> categoryExamples = categorizedExamples.get(category);
193+
if (!CollectionUtils.isNullOrEmpty(categoryExamples)) {
194+
appendCategorySection(javadoc, displayName, categoryExamples);
195+
}
196+
}
197+
198+
private void appendCategorySection(StringBuilder javadoc, String displayName,
199+
List<ExampleMetadataProvider.ExampleData> categoryExamples) {
200+
javadoc.append("<h3>").append(displayName).append("</h3>").append(System.lineSeparator());
201+
javadoc.append("<ul>").append(System.lineSeparator());
202+
203+
for (ExampleMetadataProvider.ExampleData example : categoryExamples) {
204+
javadoc.append("<li><a href=\"").append(example.getUrl()).append("\" target=\"_top\">")
205+
.append(example.getTitle()).append("</a></li>").append(System.lineSeparator());
206+
}
207+
javadoc.append("</ul>").append(System.lineSeparator());
208+
}
209+
54210
}

codegen/src/main/java/software/amazon/awssdk/codegen/internal/Constant.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public final class Constant {
9898

9999
public static final String AWS_DOCS_HOST = "docs.aws.amazon.com";
100100

101+
public static final String EXAMPLE_META_PATH = "software/amazon/awssdk/codegen/example-meta.json";
102+
101103
public static final String APPROVED_SIMPLE_METHOD_VERBS = "(get|list|describe|lookup|batchGet).*";
102104

103105
public static final String ASYNC_STREAMING_INPUT_PARAM = "requestBody";

codegen/src/main/java/software/amazon/awssdk/codegen/internal/DocumentationUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,4 @@ public static String defaultFluentReturn() {
177177
public static String defaultExistenceCheck() {
178178
return DEFAULT_EXISTENCE_CHECK;
179179
}
180-
}
180+
}

0 commit comments

Comments
 (0)