Skip to content

Commit 75c1ba7

Browse files
committed
Added support for Sync pagination
1 parent 65b6517 commit 75c1ba7

File tree

53 files changed

+3518
-132
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3518
-132
lines changed

codegen-maven-plugin/src/main/java/software/amazon/awssdk/codegen/maven/plugin/GenerationMojo.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import software.amazon.awssdk.codegen.model.config.BasicCodeGenConfig;
3636
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
3737
import software.amazon.awssdk.codegen.model.intermediate.ServiceExamples;
38+
import software.amazon.awssdk.codegen.model.service.Paginators;
3839
import software.amazon.awssdk.codegen.model.service.ServiceModel;
3940
import software.amazon.awssdk.codegen.model.service.Waiters;
4041
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils;
@@ -50,6 +51,7 @@ public class GenerationMojo extends AbstractMojo {
5051
private static final String CUSTOMIZATION_CONFIG_FILE = "customization.config";
5152
private static final String EXAMPLES_FILE = "examples-1.json";
5253
private static final String WAITERS_FILE = "waiters-2.json";
54+
private static final String PAGINATORS_FILE = "paginators.json";
5355

5456
@Parameter(property = "codeGenResources", defaultValue = "${basedir}/src/main/resources/codegen-resources/")
5557
private File codeGenResources;
@@ -75,6 +77,7 @@ public void execute() throws MojoExecutionException {
7577
.customizationConfig(loadCustomizationConfig(p))
7678
.serviceModel(loadServiceModel(p))
7779
.waitersModel(loadWaiterModel(p))
80+
.paginatorsModel(loadPaginatorModel(p))
7881
.examplesModel(loadExamplesModel(p))
7982
.build());
8083
} catch (MojoExecutionException e) {
@@ -134,6 +137,10 @@ private Waiters loadWaiterModel(Path root) {
134137
return loadOptionalModel(Waiters.class, root.resolve(WAITERS_FILE)).orElse(Waiters.NONE);
135138
}
136139

140+
private Paginators loadPaginatorModel(Path root) {
141+
return loadOptionalModel(Paginators.class, root.resolve(PAGINATORS_FILE)).orElse(Paginators.NONE);
142+
}
143+
137144
/**
138145
* Load required model from the project resources.
139146
*/

codegen/src/main/java/software/amazon/awssdk/codegen/AddMetadata.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public static Metadata constructMetadata(ServiceModel serviceModel,
7777
.withModelPackageName(Utils.getModelPackageName(serviceName, customizationConfig))
7878
.withTransformPackageName(Utils.getTransformPackageName(serviceName, customizationConfig))
7979
.withRequestTransformPackageName(Utils.getRequestTransformPackageName(serviceName, customizationConfig))
80+
.withPaginatorsPackageName(Utils.getPaginatorsPackageName(serviceName, customizationConfig))
8081
.withSmokeTestsPackageName(Utils.getSmokeTestPackageName(serviceName, customizationConfig))
8182
.withServiceAbbreviation(serviceMetadata.getServiceAbbreviation())
8283
.withServiceFullName(serviceMetadata.getServiceFullName())

codegen/src/main/java/software/amazon/awssdk/codegen/AddOperations.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import software.amazon.awssdk.codegen.model.service.Member;
3131
import software.amazon.awssdk.codegen.model.service.Operation;
3232
import software.amazon.awssdk.codegen.model.service.Output;
33+
import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
3334
import software.amazon.awssdk.codegen.model.service.ServiceModel;
3435
import software.amazon.awssdk.codegen.model.service.Shape;
3536
import software.amazon.awssdk.codegen.naming.NamingStrategy;
@@ -41,10 +42,12 @@ final class AddOperations {
4142

4243
private final ServiceModel serviceModel;
4344
private final NamingStrategy namingStrategy;
45+
private final Map<String, PaginatorDefinition> paginators;
4446

4547
AddOperations(IntermediateModelBuilder builder) {
4648
this.serviceModel = builder.getService();
4749
this.namingStrategy = builder.getNamingStrategy();
50+
this.paginators = builder.getPaginators().getPaginators();
4851
}
4952

5053
private static boolean isAuthenticated(Operation op) {
@@ -151,6 +154,7 @@ public Map<String, OperationModel> constructOperations() {
151154
operationModel.setDeprecated(op.isDeprecated());
152155
operationModel.setDocumentation(op.getDocumentation());
153156
operationModel.setIsAuthenticated(isAuthenticated(op));
157+
operationModel.setPaginated(isPaginated(op));
154158

155159
final Input input = op.getInput();
156160
if (input != null) {
@@ -219,4 +223,8 @@ private Integer getHttpStatusCode(ErrorMap error, Shape shape) {
219223
private Integer getHttpStatusCode(ErrorTrait errorTrait) {
220224
return errorTrait == null ? null : errorTrait.getHttpStatusCode();
221225
}
226+
227+
private boolean isPaginated(Operation op) {
228+
return paginators.keySet().contains(op.getName()) && paginators.get(op.getName()).isValid();
229+
}
222230
}

codegen/src/main/java/software/amazon/awssdk/codegen/C2jModels.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import software.amazon.awssdk.codegen.model.config.BasicCodeGenConfig;
1919
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
2020
import software.amazon.awssdk.codegen.model.intermediate.ServiceExamples;
21+
import software.amazon.awssdk.codegen.model.service.Paginators;
2122
import software.amazon.awssdk.codegen.model.service.ServiceModel;
2223
import software.amazon.awssdk.codegen.model.service.Waiters;
2324

@@ -31,14 +32,17 @@ public class C2jModels {
3132
private final ServiceExamples examplesModel;
3233
private final BasicCodeGenConfig codeGenConfig;
3334
private final CustomizationConfig customizationConfig;
35+
private final Paginators paginatorsModel;
3436

3537
private C2jModels(ServiceModel serviceModel, Waiters waitersModel, ServiceExamples examplesModel,
36-
BasicCodeGenConfig codeGenConfig, CustomizationConfig customizationConfig) {
38+
BasicCodeGenConfig codeGenConfig, CustomizationConfig customizationConfig,
39+
Paginators paginatorsModel) {
3740
this.serviceModel = serviceModel;
3841
this.waitersModel = waitersModel;
3942
this.examplesModel = examplesModel;
4043
this.codeGenConfig = codeGenConfig;
4144
this.customizationConfig = customizationConfig;
45+
this.paginatorsModel = paginatorsModel;
4246
}
4347

4448
public static Builder builder() {
@@ -65,13 +69,18 @@ public CustomizationConfig customizationConfig() {
6569
return customizationConfig;
6670
}
6771

72+
public Paginators paginatorsModel() {
73+
return paginatorsModel;
74+
}
75+
6876
public static class Builder {
6977

7078
private ServiceModel serviceModel;
7179
private Waiters waitersModel;
7280
private ServiceExamples examplesModel;
7381
private BasicCodeGenConfig codeGenConfig;
7482
private CustomizationConfig customizationConfig;
83+
private Paginators paginatorsModel;
7584

7685
private Builder() {
7786
}
@@ -101,10 +110,16 @@ public Builder customizationConfig(CustomizationConfig customizationConfig) {
101110
return this;
102111
}
103112

113+
public Builder paginatorsModel(Paginators paginatorsModel) {
114+
this.paginatorsModel = paginatorsModel;
115+
return this;
116+
}
117+
104118
public C2jModels build() {
105119
final Waiters waiters = waitersModel != null ? waitersModel : Waiters.NONE;
120+
final Paginators paginators = paginatorsModel != null ? paginatorsModel : Paginators.NONE;
106121
final ServiceExamples examples = examplesModel != null ? examplesModel : ServiceExamples.NONE;
107-
return new C2jModels(serviceModel, waiters, examples, codeGenConfig, customizationConfig);
122+
return new C2jModels(serviceModel, waiters, examples, codeGenConfig, customizationConfig, paginators);
108123
}
109124
}
110125
}

codegen/src/main/java/software/amazon/awssdk/codegen/IntermediateModelBuilder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
4444
import software.amazon.awssdk.codegen.model.service.AuthType;
4545
import software.amazon.awssdk.codegen.model.service.Operation;
46+
import software.amazon.awssdk.codegen.model.service.Paginators;
4647
import software.amazon.awssdk.codegen.model.service.ServiceModel;
4748
import software.amazon.awssdk.codegen.naming.DefaultNamingStrategy;
4849
import software.amazon.awssdk.codegen.naming.NamingStrategy;
@@ -61,6 +62,7 @@ public class IntermediateModelBuilder {
6162
private final NamingStrategy namingStrategy;
6263
private final TypeUtils typeUtils;
6364
private final List<IntermediateModelShapeProcessor> shapeProcessors;
65+
private final Paginators paginators;
6466

6567
public IntermediateModelBuilder(C2jModels models) {
6668
this.customConfig = models.customizationConfig();
@@ -70,6 +72,7 @@ public IntermediateModelBuilder(C2jModels models) {
7072
this.namingStrategy = new DefaultNamingStrategy(service, customConfig);
7173
this.typeUtils = new TypeUtils(namingStrategy);
7274
this.shapeProcessors = createShapeProcessors();
75+
this.paginators = models.paginatorsModel();
7376
}
7477

7578

@@ -116,7 +119,7 @@ public IntermediateModel build() throws IOException {
116119

117120
IntermediateModel fullModel = new IntermediateModel(
118121
constructMetadata(service, codeGenConfig, customConfig), operations, shapes,
119-
customConfig, examples, authorizers);
122+
customConfig, examples, authorizers, paginators.getPaginators());
120123

121124
customization.postprocess(fullModel);
122125

@@ -131,7 +134,8 @@ public IntermediateModel build() throws IOException {
131134
trimmedShapes,
132135
fullModel.getCustomizationConfig(),
133136
fullModel.getExamples(),
134-
fullModel.getCustomAuthorizers());
137+
fullModel.getCustomAuthorizers(),
138+
fullModel.getPaginators());
135139

136140
linkMembersToShapes(trimmedModel);
137141
linkOperationsToInputOutputShapes(trimmedModel);
@@ -249,4 +253,7 @@ public TypeUtils getTypeUtils() {
249253
return typeUtils;
250254
}
251255

256+
public Paginators getPaginators() {
257+
return paginators;
258+
}
252259
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ abstract class OperationDocProvider {
5151

5252
protected final IntermediateModel model;
5353
protected final OperationModel opModel;
54+
protected final PaginationDocs paginationDocs;
5455

5556
OperationDocProvider(IntermediateModel model, OperationModel opModel) {
5657
this.model = model;
5758
this.opModel = opModel;
59+
this.paginationDocs = new PaginationDocs(model, opModel);
5860
}
5961

6062
/**
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.codegen.docs;
17+
18+
import com.squareup.javapoet.ClassName;
19+
import com.squareup.javapoet.CodeBlock;
20+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
21+
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
22+
import software.amazon.awssdk.codegen.poet.PoetExtensions;
23+
import software.amazon.awssdk.codegen.utils.PaginatorUtils;
24+
25+
public class PaginationDocs {
26+
27+
private final OperationModel operationModel;
28+
private final PoetExtensions poetExtensions;
29+
30+
public PaginationDocs(IntermediateModel intermediateModel, OperationModel operationModel) {
31+
this.operationModel = operationModel;
32+
this.poetExtensions = new PoetExtensions(intermediateModel);
33+
}
34+
35+
/**
36+
* Constructs additional documentation on the client operation that is appended to the service documentation.
37+
*
38+
* TODO Add a link to our developer guide which will have more details and sample code. Write a blog post too.
39+
*/
40+
public String getDocsForSyncOperation() {
41+
return CodeBlock.builder()
42+
.add("<p>This is a variant of {@link #$L($T)} operation. "
43+
+ "The return type is a custom iterable that can be used to iterate through all the pages. "
44+
+ "SDK will internally handle making service calls for you.\n</p>",
45+
operationModel.getMethodName(), requestType())
46+
.add("<p>\nWhen this operation is called, a custom iterable is returned but no service calls are "
47+
+ "made yet. So there is no guarantee that the request is valid. As you iterate "
48+
+ "through the iterable, SDK will start lazily loading response pages by making service calls until "
49+
+ "there are no pages left or your iteration stops. If there are errors in your request, you will "
50+
+ "see the failures only after you start iterating through the iterable.\n</p>")
51+
.add(getSyncCodeSnippets())
52+
.build()
53+
.toString();
54+
}
55+
56+
/**
57+
* Constructs javadocs for the generated response classes in a paginated operation.
58+
* @param clientInterface A java poet {@link ClassName} type of the sync client interface
59+
*/
60+
public String getDocsForSyncResponseClass(ClassName clientInterface) {
61+
return CodeBlock.builder()
62+
.add("<p>Represents the output for the {@link $T#$L($T)} operation which is a paginated operation."
63+
+ " This class is an iterable of {@link $T} that can be used to iterate through all the "
64+
+ "response pages of the operation.</p>",
65+
clientInterface, getSyncPaginatedMethodName(), requestType(), syncResponsePageType())
66+
.add("<p>When the operation is called, an instance of this class is returned. At this point, "
67+
+ "no service calls are made yet and so there is no guarantee that the request is valid. "
68+
+ "As you iterate through the iterable, SDK will start lazily loading response pages by making "
69+
+ "service calls until there are no pages left or your iteration stops. If there are errors in your "
70+
+ "request, you will see the failures only after you start iterating through the iterable.</p>")
71+
.add(getSyncCodeSnippets())
72+
.build()
73+
.toString();
74+
}
75+
76+
private String getSyncCodeSnippets() {
77+
CodeBlock callOperationOnClient = CodeBlock.builder()
78+
.addStatement("$T responses = client.$L(request)", syncPaginatedResponseType(),
79+
getSyncPaginatedMethodName())
80+
.build();
81+
82+
return CodeBlock.builder()
83+
.add("\n\n<p>The following are few ways to iterate through the response pages:</p>")
84+
.add("1) Using a Stream")
85+
.add(buildCode(CodeBlock.builder()
86+
.add(callOperationOnClient)
87+
.addStatement("responses.stream().forEach(....)")
88+
.build()))
89+
.add("\n\n2) Using For loop")
90+
.add(buildCode(CodeBlock.builder()
91+
.add(callOperationOnClient)
92+
.beginControlFlow("for ($T response : responses)", syncResponsePageType())
93+
.addStatement(" // do something")
94+
.endControlFlow()
95+
.build()))
96+
.add("\n\n3) Use iterator directly")
97+
.add(buildCode(CodeBlock.builder()
98+
.add(callOperationOnClient)
99+
.addStatement("responses.iterator().forEachRemaining(....)")
100+
.build()))
101+
.add(noteAboutSyncNonPaginatedMethod())
102+
.build()
103+
.toString();
104+
}
105+
106+
private CodeBlock buildCode(CodeBlock codeSnippet) {
107+
return CodeBlock.builder()
108+
.add("<pre>{@code\n")
109+
.add(codeSnippet)
110+
.add("}</pre>")
111+
.build();
112+
113+
}
114+
115+
/**
116+
* @return Method name for the sync paginated operation
117+
*/
118+
private String getSyncPaginatedMethodName() {
119+
return PaginatorUtils.getSyncMethodName(operationModel.getMethodName());
120+
}
121+
122+
/**
123+
* @return A Poet {@link ClassName} for the sync operation request type.
124+
*
125+
* Example: For ListTables operation, it will be "ListTablesRequest" class.
126+
*/
127+
private ClassName requestType() {
128+
return poetExtensions.getModelClass(operationModel.getInput().getVariableType());
129+
}
130+
131+
/**
132+
* @return A Poet {@link ClassName} for the return type of sync non-paginated operation.
133+
*
134+
* Example: For ListTables operation, it will be "ListTablesResponse" class.
135+
*/
136+
private ClassName syncResponsePageType() {
137+
return poetExtensions.getModelClass(operationModel.getReturnType().getReturnType());
138+
}
139+
140+
/**
141+
* @return A Poet {@link ClassName} for the return type of sync paginated operation.
142+
*/
143+
public ClassName syncPaginatedResponseType() {
144+
return poetExtensions.getResponseClassForPaginatedSyncOperation(operationModel.getOperationName());
145+
}
146+
147+
private CodeBlock noteAboutSyncNonPaginatedMethod() {
148+
return CodeBlock.builder()
149+
.add("\n<p><b>Note: If you prefer to have control on service calls, use the {@link #$L($T)} operation."
150+
+ "</b></p>", operationModel.getMethodName(), requestType())
151+
.build();
152+
}
153+
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,21 @@ public enum SimpleMethodOverload {
2626
*/
2727
NORMAL,
2828

29+
/**
30+
* The standard paginated method overload that takes in a request object and returns a response object.
31+
*/
32+
PAGINATED,
33+
2934
/**
3035
* Simple method that takes no arguments and creates an empty request object that delegates to the {@link #NORMAL} overload.
3136
*/
3237
NO_ARG,
3338

39+
/**
40+
* Paginated simple method that takes no arguments and creates an empty request object.
41+
*/
42+
NO_ARG_PAGINATED,
43+
3444
/**
3545
* Simple method for streaming operations (input or output) that takes in the request object and a file to
3646
* upload from or download to.

0 commit comments

Comments
 (0)