From 3b12e2432aa3d6cff4d5e35bb85849fbae5ec4f9 Mon Sep 17 00:00:00 2001
From: Varun Nandi
Date: Wed, 6 Dec 2017 12:53:14 -0800
Subject: [PATCH] Support Async pagination
---
.../amazon/awssdk/spotbugs-suppressions.xml | 7 +
.../docs/AsyncOperationDocProvider.java | 48 +-
.../codegen/docs/DocumentationBuilder.java | 2 +
.../awssdk/codegen/docs/PaginationDocs.java | 127 ++++-
.../docs/SyncOperationDocProvider.java | 11 +-
.../tasks/PaginatorsGeneratorTasks.java | 23 +-
.../awssdk/codegen/poet/PoetExtensions.java | 15 +-
.../codegen/poet/client/AsyncClientClass.java | 9 +
.../poet/client/AsyncClientInterface.java | 59 +++
.../codegen/poet/client/SyncClientClass.java | 2 +-
.../poet/client/SyncClientInterface.java | 4 +-
.../paginators/AsyncResponseClassSpec.java | 257 ++++++++++
.../PaginatorResponseClassSpec.java | 459 ------------------
.../poet/paginators/PaginatorsClassSpec.java | 365 ++++++++++++++
.../paginators/SyncResponseClassSpec.java | 226 +++++++++
.../awssdk/codegen/utils/PaginatorUtils.java | 6 +-
...va => PaginatedResponseClassSpecTest.java} | 30 +-
.../poet/client/c2j/json/customization.config | 3 +-
.../poet/client/c2j/json/service-2.json | 3 -
.../poet/client/test-async-client-class.java | 254 ++++++++--
.../test-json-async-client-interface.java | 274 ++++++++++-
.../poet/client/test-json-client-class.java | 160 +++---
.../client/test-json-client-interface.java | 185 +++++--
...inatedOperationWithResultKeyIterable.java} | 54 ++-
...inatedOperationWithResultKeyPublisher.java | 150 ++++++
...tedOperationWithoutResultKeyIterable.java} | 44 +-
...tedOperationWithoutResultKeyPublisher.java | 132 +++++
.../poet/paginators/customization.config | 3 +-
.../codegen/poet/paginators/service-2.json | 3 -
.../pagination/PaginatedItemsIterable.java | 13 +-
.../PaginatedResponsesIterator.java | 4 +-
...tPageFetcher.java => SyncPageFetcher.java} | 2 +-
.../pagination/async/AsyncPageFetcher.java | 44 ++
.../pagination/async/EmptySubscription.java | 63 +++
.../pagination/async/ItemsSubscription.java | 103 ++++
.../async/PaginatedItemsPublisher.java | 50 ++
.../async/PaginationSubscription.java | 104 ++++
.../async/ResponsesSubscription.java | 62 +++
.../core/pagination/async/SdkPublisher.java | 33 ++
.../async/SequentialSubscriber.java | 63 +++
.../PaginatedItemsIterableTest.java | 12 +-
pom.xml | 5 +
.../ScanPaginatorIntegrationTest.java | 84 +++-
services/pom.xml | 5 +
...istObjectsV2PaginatorsIntegrationTest.java | 307 ++++++++++++
45 files changed, 3113 insertions(+), 756 deletions(-)
create mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/AsyncResponseClassSpec.java
delete mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpec.java
create mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorsClassSpec.java
create mode 100644 codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/SyncResponseClassSpec.java
rename codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/{PaginatorResponseClassSpecTest.java => PaginatedResponseClassSpecTest.java} (71%)
rename codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/{PaginatedOperationWithResultKeyPaginator.java => PaginatedOperationWithResultKeyIterable.java} (65%)
create mode 100644 codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPublisher.java
rename codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/{PaginatedOperationWithoutResultKeyPaginator.java => PaginatedOperationWithoutResultKeyIterable.java} (62%)
create mode 100644 codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPublisher.java
rename core/src/main/java/software/amazon/awssdk/core/pagination/{NextPageFetcher.java => SyncPageFetcher.java} (96%)
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/AsyncPageFetcher.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/EmptySubscription.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/ItemsSubscription.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginatedItemsPublisher.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginationSubscription.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/ResponsesSubscription.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/SdkPublisher.java
create mode 100644 core/src/main/java/software/amazon/awssdk/core/pagination/async/SequentialSubscriber.java
create mode 100644 services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsV2PaginatorsIntegrationTest.java
diff --git a/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml b/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml
index e7a1624f7e82..7c6c2f6ca2c7 100644
--- a/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml
+++ b/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml
@@ -60,4 +60,11 @@
wrap it in an sdk-specific exception. -->
+
+
+
+
+
+
+
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/AsyncOperationDocProvider.java b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/AsyncOperationDocProvider.java
index 05da2f72d256..80be3fd23f31 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/AsyncOperationDocProvider.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/AsyncOperationDocProvider.java
@@ -16,9 +16,9 @@
package software.amazon.awssdk.codegen.docs;
import java.util.Map;
+import software.amazon.awssdk.codegen.internal.ImmutableMapParameter;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
-import software.amazon.awssdk.core.util.ImmutableMapParameter;
/**
* Implementations of {@link OperationDocProvider} for async client methods. This implementation is for the typical
@@ -86,10 +86,14 @@ protected void applyThrows(DocumentationBuilder docBuilder) {
* @return Factories to use for the {@link ClientType#ASYNC} method type.
*/
static Map asyncFactories() {
- return ImmutableMapParameter.of(SimpleMethodOverload.NORMAL, AsyncOperationDocProvider::new,
- SimpleMethodOverload.NO_ARG, AsyncNoArg::new,
- SimpleMethodOverload.FILE, AsyncFile::new,
- SimpleMethodOverload.CONSUMER_BUILDER, AsyncConsumerBuilder::new);
+ return new ImmutableMapParameter.Builder()
+ .put(SimpleMethodOverload.NORMAL, AsyncOperationDocProvider::new)
+ .put(SimpleMethodOverload.NO_ARG, AsyncNoArg::new)
+ .put(SimpleMethodOverload.FILE, AsyncFile::new)
+ .put(SimpleMethodOverload.CONSUMER_BUILDER, AsyncConsumerBuilder::new)
+ .put(SimpleMethodOverload.PAGINATED, AsyncPaginated::new)
+ .put(SimpleMethodOverload.NO_ARG_PAGINATED, AsyncPaginatedNoArg::new)
+ .build();
}
/**
@@ -150,4 +154,38 @@ protected void applyParams(DocumentationBuilder docBuilder) {
opModel.getInputShape().getC2jName());
}
}
+
+ /**
+ * Provider for traditional paginated method that takes in a request object and returns a response object.
+ */
+ private static class AsyncPaginated extends AsyncOperationDocProvider {
+
+ private AsyncPaginated(IntermediateModel model, OperationModel opModel) {
+ super(model, opModel);
+ }
+
+ @Override
+ protected String appendToDescription() {
+ return paginationDocs.getDocsForAsyncOperation();
+ }
+
+ @Override
+ protected void applyReturns(DocumentationBuilder docBuilder) {
+ docBuilder.returns("A custom publisher that can be subscribed to request a stream of response pages.");
+ }
+ }
+
+ /**
+ * Provider for paginated simple method that takes no arguments and creates an empty request object.
+ */
+ private static class AsyncPaginatedNoArg extends AsyncPaginated {
+
+ private AsyncPaginatedNoArg(IntermediateModel model, OperationModel opModel) {
+ super(model, opModel);
+ }
+
+ @Override
+ protected void applyParams(DocumentationBuilder docBuilder) {
+ }
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/DocumentationBuilder.java b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/DocumentationBuilder.java
index a68f391d6444..fcf62d4d7c5c 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/DocumentationBuilder.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/DocumentationBuilder.java
@@ -30,6 +30,8 @@
*/
public final class DocumentationBuilder {
+ // TODO This prefix is not suitable for paginated operations. Either remove it for paginated operations
+ // or change the statement to something generic
private static final String ASYNC_THROWS_PREFIX = "The CompletableFuture returned by this method can be completed " +
"exceptionally with the following exceptions.";
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/PaginationDocs.java b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/PaginationDocs.java
index 6413e2fabcd9..86a3d49f2166 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/PaginationDocs.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/PaginationDocs.java
@@ -17,13 +17,20 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.poet.PoetExtensions;
import software.amazon.awssdk.codegen.utils.PaginatorUtils;
+import software.amazon.awssdk.core.pagination.async.SequentialSubscriber;
public class PaginationDocs {
+ private static final String SUBSCRIBE_METHOD_NAME = "subscribe";
+
private final OperationModel operationModel;
private final PoetExtensions poetExtensions;
@@ -62,7 +69,7 @@ public String getDocsForSyncResponseClass(ClassName clientInterface) {
.add("Represents the output for the {@link $T#$L($T)} operation which is a paginated operation."
+ " This class is an iterable of {@link $T} that can be used to iterate through all the "
+ "response pages of the operation.
",
- clientInterface, getSyncPaginatedMethodName(), requestType(), syncResponsePageType())
+ clientInterface, getPaginatedMethodName(), requestType(), syncResponsePageType())
.add("When the operation is called, an instance of this class is returned. At this point, "
+ "no service calls are made yet and so there is no guarantee that the request is valid. "
+ "As you iterate through the iterable, SDK will start lazily loading response pages by making "
@@ -73,10 +80,54 @@ clientInterface, getSyncPaginatedMethodName(), requestType(), syncResponsePageTy
.toString();
}
+ /**
+ * Constructs additional documentation on the async client operation that is appended to the service documentation.
+ */
+ public String getDocsForAsyncOperation() {
+ return CodeBlock.builder()
+ .add("
This is a variant of {@link #$L($T)} operation. "
+ + "The return type is a custom publisher that can be subscribed to request a stream of response "
+ + "pages. SDK will internally handle making service calls for you.\n
",
+ operationModel.getMethodName(), requestType())
+ .add("When the operation is called, an instance of this class is returned. At this point, "
+ + "no service calls are made yet and so there is no guarantee that the request is valid. "
+ + "If there are errors in your request, you will see the failures only after you start streaming "
+ + "the data. The subscribe method should be called as a request to start streaming data. "
+ + "For more info, see {@link $T#$L($T)}. Each call to the subscribe method will result in a new "
+ + "{@link $T} i.e., a new contract to stream data from the starting request.
",
+ getPublisherType(), SUBSCRIBE_METHOD_NAME, getSubscriberType(), getSubscriptionType())
+ .add(getAsyncCodeSnippets())
+ .build()
+ .toString();
+ }
+
+ /**
+ * Constructs javadocs for the generated response classes of a paginated operation in Async client.
+ * @param clientInterface A java poet {@link ClassName} type of the Async client interface
+ */
+ public String getDocsForAsyncResponseClass(ClassName clientInterface) {
+ return CodeBlock.builder()
+ .add("Represents the output for the {@link $T#$L($T)} operation which is a paginated operation."
+ + " This class is a type of {@link $T} which can be used to provide a sequence of {@link $T} "
+ + "response pages as per demand from the subscriber.
",
+ clientInterface, getPaginatedMethodName(), requestType(), getPublisherType(),
+ syncResponsePageType())
+ .add("When the operation is called, an instance of this class is returned. At this point, "
+ + "no service calls are made yet and so there is no guarantee that the request is valid. "
+ + "If there are errors in your request, you will see the failures only after you start streaming "
+ + "the data. The subscribe method should be called as a request to start streaming data. "
+ + "For more info, see {@link $T#$L($T)}. Each call to the subscribe method will result in a new "
+ + "{@link $T} i.e., a new contract to stream data from the starting request.
",
+ getPublisherType(), SUBSCRIBE_METHOD_NAME, getSubscriberType(), getSubscriptionType())
+ .add(getAsyncCodeSnippets())
+ .build()
+ .toString();
+ }
+
private String getSyncCodeSnippets() {
CodeBlock callOperationOnClient = CodeBlock.builder()
.addStatement("$T responses = client.$L(request)", syncPaginatedResponseType(),
- getSyncPaginatedMethodName())
+ getPaginatedMethodName())
.build();
return CodeBlock.builder()
@@ -103,20 +154,56 @@ private String getSyncCodeSnippets() {
.toString();
}
+ private String getAsyncCodeSnippets() {
+ CodeBlock callOperationOnClient = CodeBlock.builder()
+ .addStatement("$T publisher = client.$L(request)",
+ asyncPaginatedResponseType(),
+ getPaginatedMethodName())
+ .build();
+
+ return CodeBlock.builder()
+ .add("\n\nThe following are few ways to use the response class:
")
+ .add("1) Using the forEach helper method",
+ TypeName.get(SequentialSubscriber.class))
+ .add(buildCode(CodeBlock.builder()
+ .add(callOperationOnClient)
+ .add(CodeBlock.builder()
+ .addStatement("CompletableFuture future = publisher"
+ + ".forEach(res -> "
+ + "{ // Do something with the response })")
+ .addStatement("future.get()")
+ .build())
+ .build()))
+ .add("\n\n2) Using a custom subscriber")
+ .add(buildCode(CodeBlock.builder()
+ .add(callOperationOnClient)
+ .add("publisher.subscribe(new Subscriber<$T>() {\n\n", syncResponsePageType())
+ .addStatement("public void onSubscribe($T subscription) { //... }",
+ getSubscriberType())
+ .add("\n\n")
+ .addStatement("public void onNext($T response) { //... }", syncResponsePageType())
+ .add("});")
+ .build()))
+ .add("As the response is a publisher, it can work well with third party reactive streams implementations "
+ + "like RxJava2.")
+ .add(noteAboutSyncNonPaginatedMethod())
+ .build()
+ .toString();
+ }
+
private CodeBlock buildCode(CodeBlock codeSnippet) {
return CodeBlock.builder()
.add("{@code\n")
.add(codeSnippet)
.add("}
")
.build();
-
}
/**
* @return Method name for the sync paginated operation
*/
- private String getSyncPaginatedMethodName() {
- return PaginatorUtils.getSyncMethodName(operationModel.getMethodName());
+ private String getPaginatedMethodName() {
+ return PaginatorUtils.getPaginatedMethodName(operationModel.getMethodName());
}
/**
@@ -140,14 +227,42 @@ private ClassName syncResponsePageType() {
/**
* @return A Poet {@link ClassName} for the return type of sync paginated operation.
*/
- public ClassName syncPaginatedResponseType() {
+ private ClassName syncPaginatedResponseType() {
return poetExtensions.getResponseClassForPaginatedSyncOperation(operationModel.getOperationName());
}
+ /**
+ * @return A Poet {@link ClassName} for the return type of Async paginated operation.
+ */
+ private ClassName asyncPaginatedResponseType() {
+ return poetExtensions.getResponseClassForPaginatedAsyncOperation(operationModel.getOperationName());
+ }
+
private CodeBlock noteAboutSyncNonPaginatedMethod() {
return CodeBlock.builder()
.add("\nNote: If you prefer to have control on service calls, use the {@link #$L($T)} operation."
+ "
", operationModel.getMethodName(), requestType())
.build();
}
+
+ /**
+ * @return A Poet {@link ClassName} for the reactive streams {@link Publisher}.
+ */
+ private ClassName getPublisherType() {
+ return ClassName.get(Publisher.class);
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the reactive streams {@link Subscriber}.
+ */
+ private ClassName getSubscriberType() {
+ return ClassName.get(Subscriber.class);
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the reactive streams {@link Subscription}.
+ */
+ private ClassName getSubscriptionType() {
+ return ClassName.get(Subscription.class);
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/SyncOperationDocProvider.java b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/SyncOperationDocProvider.java
index c329bedf3258..7bd82ecdb44f 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/docs/SyncOperationDocProvider.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/docs/SyncOperationDocProvider.java
@@ -204,7 +204,7 @@ protected void applyParams(DocumentationBuilder docBuilder) {
}
/**
- * Provider for standard paginated method that that takes in a request object and returns a response object.
+ * Provider for standard paginated method that takes in a request object and returns a response object.
*/
private static class SyncPaginated extends SyncOperationDocProvider {
@@ -214,7 +214,12 @@ private SyncPaginated(IntermediateModel model, OperationModel opModel) {
@Override
protected String appendToDescription() {
- return opModel.isPaginated() ? paginationDocs.getDocsForSyncOperation() : null;
+ return paginationDocs.getDocsForSyncOperation();
+ }
+
+ @Override
+ protected void applyReturns(DocumentationBuilder docBuilder) {
+ docBuilder.returns("A custom iterable that can be used to iterate through all the response pages.");
}
}
@@ -230,7 +235,7 @@ private SyncPaginatedNoArg(IntermediateModel model, OperationModel opModel) {
@Override
protected void applyParams(DocumentationBuilder docBuilder) {
// Link to non-simple method for discoverability
- docBuilder.see("#%s(%s)", PaginatorUtils.getSyncMethodName(opModel.getMethodName()),
+ docBuilder.see("#%s(%s)", PaginatorUtils.getPaginatedMethodName(opModel.getMethodName()),
opModel.getInput().getVariableType());
}
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/PaginatorsGeneratorTasks.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/PaginatorsGeneratorTasks.java
index f353b70bd619..12a3314109d1 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/PaginatorsGeneratorTasks.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/PaginatorsGeneratorTasks.java
@@ -18,15 +18,18 @@
import static software.amazon.awssdk.utils.FunctionalUtils.safeFunction;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask;
import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
import software.amazon.awssdk.codegen.poet.ClassSpec;
-import software.amazon.awssdk.codegen.poet.paginators.PaginatorResponseClassSpec;
+import software.amazon.awssdk.codegen.poet.paginators.AsyncResponseClassSpec;
+import software.amazon.awssdk.codegen.poet.paginators.SyncResponseClassSpec;
public class PaginatorsGeneratorTasks extends BaseGeneratorTasks {
@@ -44,15 +47,25 @@ protected boolean hasTasks() {
@Override
protected List createTasks() throws Exception {
- info("Emitting paginator classes");
return model.getPaginators().entrySet().stream()
.filter(entry -> entry.getValue().isValid())
- .map(safeFunction(this::createTask))
+ .flatMap(safeFunction(this::createSyncAndAsyncTasks))
.collect(Collectors.toList());
}
- private GeneratorTask createTask(Map.Entry entry) throws IOException {
- ClassSpec classSpec = new PaginatorResponseClassSpec(model, entry.getKey(), entry.getValue());
+ private Stream createSyncAndAsyncTasks(Map.Entry entry) throws IOException {
+ return Arrays.asList(createSyncTask(entry), createAsyncTask(entry))
+ .stream();
+ }
+
+ private GeneratorTask createSyncTask(Map.Entry entry) throws IOException {
+ ClassSpec classSpec = new SyncResponseClassSpec(model, entry.getKey(), entry.getValue());
+
+ return new PoetGeneratorTask(paginatorsClassDir, model.getFileHeader(), classSpec);
+ }
+
+ private GeneratorTask createAsyncTask(Map.Entry entry) throws IOException {
+ ClassSpec classSpec = new AsyncResponseClassSpec(model, entry.getKey(), entry.getValue());
return new PoetGeneratorTask(paginatorsClassDir, model.getFileHeader(), classSpec);
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtensions.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtensions.java
index 53987863a81c..cdefdf41ed9f 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtensions.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/PoetExtensions.java
@@ -67,11 +67,22 @@ public ClassName getClientClass(String className) {
* @return A Poet {@link ClassName} for the response type of a paginated operation in the base service package.
*
* Example: If operationName is "ListTables", then the response type of the paginated operation
- * will be "ListTablesPaginator" class in the base service package.
+ * will be "ListTablesIterable" class.
*/
@ReviewBeforeRelease("Naming of response shape for paginated APIs")
public ClassName getResponseClassForPaginatedSyncOperation(String operationName) {
- return ClassName.get(model.getMetadata().getFullPaginatorsPackageName(), operationName + "Paginator");
+ return ClassName.get(model.getMetadata().getFullPaginatorsPackageName(), operationName + "Iterable");
}
+ /**
+ * @param operationName Name of the operation
+ * @return A Poet {@link ClassName} for the response type of a async paginated operation in the base service package.
+ *
+ * Example: If operationName is "ListTables", then the async response type of the paginated operation
+ * will be "ListTablesPublisher" class.
+ */
+ @ReviewBeforeRelease("Naming of response shape for paginated APIs")
+ public ClassName getResponseClassForPaginatedAsyncOperation(String operationName) {
+ return ClassName.get(model.getMetadata().getFullPaginatorsPackageName(), operationName + "Publisher");
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
index 4cd3ca9ee31d..d3dd9254f395 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
@@ -60,6 +60,7 @@ public TypeSpec poetSpec() {
interfaceClass)
.addMethod(nameMethod())
.addMethods(operations())
+ .addMethods(paginatedTraditionalMethods())
.addMethod(closeMethod())
.addMethods(protocolSpec.additionalMethods())
.addMethod(protocolSpec.initProtocolFactory(model));
@@ -128,6 +129,14 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
}
+ @Override
+ protected MethodSpec.Builder paginatedMethodBody(MethodSpec.Builder builder, OperationModel opModel) {
+ return builder.addModifiers(Modifier.PUBLIC)
+ .addStatement("return new $T(this, $L)",
+ poetExtensions.getResponseClassForPaginatedAsyncOperation(opModel.getOperationName()),
+ opModel.getInput().getVariableName());
+ }
+
@Override
public ClassName className() {
return className;
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientInterface.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientInterface.java
index 2811dd9fecc8..da632848b545 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientInterface.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientInterface.java
@@ -36,7 +36,9 @@
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
import software.amazon.awssdk.codegen.poet.ClassSpec;
+import software.amazon.awssdk.codegen.poet.PoetExtensions;
import software.amazon.awssdk.codegen.poet.PoetUtils;
+import software.amazon.awssdk.codegen.utils.PaginatorUtils;
import software.amazon.awssdk.core.SdkClient;
import software.amazon.awssdk.core.async.AsyncRequestProvider;
import software.amazon.awssdk.core.async.AsyncResponseHandler;
@@ -52,6 +54,7 @@ public class AsyncClientInterface implements ClassSpec {
protected final ClassName className;
protected final String clientPackageName;
private final String modelPackage;
+ private final PoetExtensions poetExtensions;
public AsyncClientInterface(IntermediateModel model) {
this.modelPackage = model.getMetadata().getFullModelPackageName();
@@ -59,6 +62,7 @@ public AsyncClientInterface(IntermediateModel model) {
this.model = model;
this.className = ClassName.get(model.getMetadata().getFullClientPackageName(),
model.getMetadata().getAsyncInterface());
+ this.poetExtensions = new PoetExtensions(model);
}
@Override
@@ -132,9 +136,64 @@ private Iterable operationsAndSimpleMethods() {
.flatMap(List::stream)
.map(MethodSpec.Builder::build)
.collect(toList()));
+
+ methods.addAll(paginatedTraditionalMethods());
+ methods.addAll(paginatedSimpleMethods());
+
return methods.stream().sorted(Comparator.comparing(m -> m.name)).collect(toList());
}
+ protected List paginatedTraditionalMethods() {
+ return model.getOperations().values().stream()
+ .filter(operationModel -> operationModel.isPaginated())
+ .map(this::paginatedTraditionalMethod)
+ .collect(toList());
+ }
+
+ private MethodSpec paginatedTraditionalMethod(OperationModel opModel) {
+ final String methodName = PaginatorUtils.getPaginatedMethodName(opModel.getMethodName());
+ final ClassName requestType = ClassName.get(modelPackage, opModel.getInput().getVariableType());
+ final ClassName responsePojoType = poetExtensions.getResponseClassForPaginatedAsyncOperation(opModel.getOperationName());
+
+ MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
+ .returns(responsePojoType)
+ .addParameter(requestType, opModel.getInput().getVariableName())
+ .addJavadoc(opModel.getDocs(model,
+ ClientType.ASYNC,
+ SimpleMethodOverload.PAGINATED));
+
+ return paginatedMethodBody(builder, opModel).build();
+ }
+
+ protected MethodSpec.Builder paginatedMethodBody(MethodSpec.Builder builder, OperationModel operationModel) {
+ return builder.addModifiers(Modifier.DEFAULT, Modifier.PUBLIC)
+ .addStatement("throw new $T()", UnsupportedOperationException.class);
+ }
+
+ private List paginatedSimpleMethods() {
+ return model.getOperations().values().stream()
+ .filter(operationModel -> operationModel.isPaginated())
+ .filter(operationModel -> operationModel.getInputShape().isSimpleMethod())
+ .map(this::paginatedSimpleMethod)
+ .collect(toList());
+ }
+
+ private MethodSpec paginatedSimpleMethod(OperationModel opModel) {
+ final String methodName = PaginatorUtils.getPaginatedMethodName(opModel.getMethodName());
+ final ClassName requestType = ClassName.get(modelPackage, opModel.getInput().getVariableType());
+ final ClassName responsePojoType = poetExtensions.getResponseClassForPaginatedAsyncOperation(opModel.getOperationName());
+
+ MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
+ .addModifiers(Modifier.DEFAULT, Modifier.PUBLIC)
+ .returns(responsePojoType)
+ .addStatement("return $L($T.builder().build())", methodName, requestType)
+ .addJavadoc(opModel.getDocs(model,
+ ClientType.ASYNC,
+ SimpleMethodOverload.NO_ARG_PAGINATED));
+
+ return builder.build();
+ }
+
/**
* @param opModel Operation to generate simple methods for.
* @return All simple method overloads for a given operation.
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
index 88f7eff90cba..3f3d36279825 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
@@ -164,7 +164,7 @@ private List paginatedMethods(OperationModel opModel) {
paginatedMethodSpecs.add(SyncClientInterface.operationMethodSignature(model,
opModel,
SimpleMethodOverload.PAGINATED,
- PaginatorUtils.getSyncMethodName(
+ PaginatorUtils.getPaginatedMethodName(
opModel.getMethodName()))
.addAnnotation(Override.class)
.returns(poetExtensions.getResponseClassForPaginatedSyncOperation(
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientInterface.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientInterface.java
index 4fdc8f751fac..9ff6302691ed 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientInterface.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientInterface.java
@@ -247,7 +247,7 @@ private List paginatedMethods(OperationModel opModel) {
paginatedMethodSpecs.add(operationMethodSignature(model,
opModel,
SimpleMethodOverload.PAGINATED,
- PaginatorUtils.getSyncMethodName(opModel.getMethodName()))
+ PaginatorUtils.getPaginatedMethodName(opModel.getMethodName()))
.returns(poetExtensions.getResponseClassForPaginatedSyncOperation(
opModel.getOperationName()))
.addModifiers(Modifier.DEFAULT)
@@ -259,7 +259,7 @@ private List paginatedMethods(OperationModel opModel) {
}
private MethodSpec paginatedSimpleMethod(OperationModel opModel) {
- String paginatedMethodName = PaginatorUtils.getSyncMethodName(opModel.getMethodName());
+ String paginatedMethodName = PaginatorUtils.getPaginatedMethodName(opModel.getMethodName());
ClassName requestType = ClassName.get(model.getMetadata().getFullModelPackageName(),
opModel.getInput().getVariableType());
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/AsyncResponseClassSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/AsyncResponseClassSpec.java
new file mode 100644
index 000000000000..786e8451ddbe
--- /dev/null
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/AsyncResponseClassSpec.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.codegen.poet.paginators;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.WildcardTypeName;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.lang.model.element.Modifier;
+import org.reactivestreams.Subscriber;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
+import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
+import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
+import software.amazon.awssdk.codegen.poet.ClassSpec;
+import software.amazon.awssdk.codegen.poet.PoetUtils;
+import software.amazon.awssdk.core.pagination.async.AsyncPageFetcher;
+import software.amazon.awssdk.core.pagination.async.EmptySubscription;
+import software.amazon.awssdk.core.pagination.async.PaginatedItemsPublisher;
+import software.amazon.awssdk.core.pagination.async.ResponsesSubscription;
+import software.amazon.awssdk.core.pagination.async.SdkPublisher;
+
+/**
+ * Java poet {@link ClassSpec} to generate the response class for async paginated operations.
+ */
+public class AsyncResponseClassSpec extends PaginatorsClassSpec {
+
+ private static final String SUBSCRIBER = "subscriber";
+ private static final String SUBSCRIBE_METHOD = "subscribe";
+ private static final String LAST_PAGE_FIELD = "isLastPage";
+ private static final String LAST_PAGE_METHOD = "withLastPage";
+
+ public AsyncResponseClassSpec(IntermediateModel model, String c2jOperationName, PaginatorDefinition paginatorDefinition) {
+ super(model, c2jOperationName, paginatorDefinition);
+ }
+
+ @Override
+ public TypeSpec poetSpec() {
+ TypeSpec.Builder specBuilder = TypeSpec.classBuilder(className())
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(PoetUtils.GENERATED)
+ .addSuperinterface(getAsyncResponseInterface())
+ .addFields(Stream.of(asyncClientInterfaceField(),
+ requestClassField(),
+ asyncPageFetcherField(),
+ lastPageField())
+ .collect(Collectors.toList()))
+ .addMethod(publicConstructor())
+ .addMethod(privateConstructor())
+ .addMethod(subscribeMethod())
+ .addMethods(getMethodSpecsForResultKeyList())
+ .addMethod(resumeMethod())
+ .addJavadoc(paginationDocs.getDocsForAsyncResponseClass(
+ getAsyncClientInterfaceName()))
+ .addType(nextPageFetcherClass());
+
+ return specBuilder.build();
+ }
+
+ @Override
+ public ClassName className() {
+ return poetExtensions.getResponseClassForPaginatedAsyncOperation(c2jOperationName);
+ }
+
+ /**
+ * Returns the interface that is implemented by the Paginated Async Response class.
+ */
+ private TypeName getAsyncResponseInterface() {
+ return ParameterizedTypeName.get(ClassName.get(SdkPublisher.class), responseType());
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the async client interface
+ */
+ private ClassName getAsyncClientInterfaceName() {
+ return poetExtensions.getClientClass(model.getMetadata().getAsyncInterface());
+ }
+
+ private FieldSpec asyncClientInterfaceField() {
+ return FieldSpec.builder(getAsyncClientInterfaceName(), CLIENT_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
+ }
+
+ private FieldSpec asyncPageFetcherField() {
+ return FieldSpec.builder(AsyncPageFetcher.class, NEXT_PAGE_FETCHER_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
+ }
+
+ private FieldSpec lastPageField() {
+ return FieldSpec.builder(boolean.class, LAST_PAGE_FIELD, Modifier.PRIVATE).build();
+ }
+
+ private MethodSpec publicConstructor() {
+ return MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(getAsyncClientInterfaceName(), CLIENT_MEMBER, Modifier.FINAL)
+ .addParameter(requestType(), REQUEST_MEMBER, Modifier.FINAL)
+ .addStatement("this($L, $L, false)", CLIENT_MEMBER, REQUEST_MEMBER)
+ .build();
+ }
+
+ private MethodSpec privateConstructor() {
+ return MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PRIVATE)
+ .addParameter(getAsyncClientInterfaceName(), CLIENT_MEMBER, Modifier.FINAL)
+ .addParameter(requestType(), REQUEST_MEMBER, Modifier.FINAL)
+ .addParameter(boolean.class, LAST_PAGE_FIELD, Modifier.FINAL)
+ .addStatement("this.$L = $L", CLIENT_MEMBER, CLIENT_MEMBER)
+ .addStatement("this.$L = $L", REQUEST_MEMBER, REQUEST_MEMBER)
+ .addStatement("this.$L = $L", LAST_PAGE_FIELD, LAST_PAGE_FIELD)
+ .addStatement("this.$L = new $L()", NEXT_PAGE_FETCHER_MEMBER, nextPageFetcherClassName())
+ .build();
+ }
+
+ /**
+ * A {@link MethodSpec} for the subscribe() method which is inherited from the interface.
+ */
+ private MethodSpec subscribeMethod() {
+ return MethodSpec.methodBuilder(SUBSCRIBE_METHOD)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(ParameterizedTypeName.get(ClassName.get(Subscriber.class),
+ WildcardTypeName.supertypeOf(responseType())),
+ SUBSCRIBER)
+ .addStatement("$L.onSubscribe(new $T($L, $L))", SUBSCRIBER, ResponsesSubscription.class,
+ SUBSCRIBER, NEXT_PAGE_FETCHER_MEMBER)
+ .build();
+ }
+
+ /**
+ * Returns iterable of {@link MethodSpec} to generate helper methods for all members
+ * in {@link PaginatorDefinition#getResultKey()}.
+ *
+ * The helper methods return a publisher that can be used to stream over the collection of result keys.
+ * These methods will only be generated if {@link PaginatorDefinition#getResultKey()} is not null and a non-empty list.
+ */
+ private Iterable getMethodSpecsForResultKeyList() {
+ if (paginatorDefinition.getResultKey() != null) {
+ return paginatorDefinition.getResultKey().stream()
+ .map(this::getMethodsSpecForSingleResultKey)
+ .collect(Collectors.toList());
+ }
+ return Collections.emptyList();
+ }
+
+ /*
+ * Generate a method spec for single element in {@link PaginatorDefinition#getResultKey()} list.
+ *
+ * If the element is "Folders" and its type is "List", generated code looks like:
+ *
+ * public SdkPublisher folders() {
+ * Function> getIterator = response -> {
+ * if (response != null && response.folders() != null) {
+ * return response.folders().iterator();
+ * }
+ * return Collections.emptyIterator();
+ * };
+ * return new PaginatedItemsPublisher(new DescribeFolderContentsResponseFetcher(), getIterator);
+ * }
+ */
+ private MethodSpec getMethodsSpecForSingleResultKey(String resultKey) {
+ TypeName resultKeyType = getTypeForResultKey(resultKey);
+ MemberModel resultKeyModel = memberModelForResponseMember(resultKey);
+
+ return MethodSpec.methodBuilder(resultKeyModel.getFluentGetterMethodName())
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(ParameterizedTypeName.get(ClassName.get(SdkPublisher.class), resultKeyType))
+ .addCode("$T getIterator = ",
+ ParameterizedTypeName.get(ClassName.get(Function.class),
+ responseType(),
+ ParameterizedTypeName.get(ClassName.get(Iterator.class),
+ resultKeyType)))
+ .addCode(getIteratorLambdaBlock(resultKey, resultKeyModel))
+ .addCode("\n")
+ .addStatement("return new $T(new $L(), getIterator, $L)",
+ PaginatedItemsPublisher.class,
+ nextPageFetcherClassName(),
+ LAST_PAGE_FIELD)
+ .addJavadoc(CodeBlock.builder()
+ .add("Returns a publisher that can be used to get a stream of data. You need to "
+ + "subscribe to the publisher to request the stream of data. The publisher "
+ + "has a helper forEach method that takes in a {@link $T} and then applies "
+ + "that consumer to each response returned by the service.",
+ TypeName.get(Consumer.class))
+ .build())
+ .build();
+ }
+
+ /**aW
+ * Generates a inner class that implements {@link AsyncPageFetcher}. This is a helper class that can be used
+ * to find if there are more pages in the response and to get the next page if exists.
+ */
+ private TypeSpec nextPageFetcherClass() {
+ return TypeSpec.classBuilder(nextPageFetcherClassName())
+ .addModifiers(Modifier.PRIVATE)
+ .addSuperinterface(ParameterizedTypeName.get(ClassName.get(AsyncPageFetcher.class), responseType()))
+ .addMethod(MethodSpec.methodBuilder(HAS_NEXT_PAGE_METHOD)
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT, Modifier.FINAL)
+ .returns(boolean.class)
+ .addStatement(hasNextPageMethodBody())
+ .build())
+ .addMethod(MethodSpec.methodBuilder(NEXT_PAGE_METHOD)
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT, Modifier.FINAL)
+ .returns(ParameterizedTypeName.get(ClassName.get(CompletableFuture.class),
+ responseType()))
+ .addCode(nextPageMethodBody())
+ .build())
+ .build();
+ }
+
+ private MethodSpec resumeMethod() {
+ return resumeMethodBuilder().addCode(CodeBlock.builder()
+ .addStatement("return $L", anonymousClassWithEmptySubscription())
+ .build())
+ .build();
+ }
+
+ private TypeSpec anonymousClassWithEmptySubscription() {
+ return TypeSpec.anonymousClassBuilder("$L, $L, true", CLIENT_MEMBER, REQUEST_MEMBER)
+ .addSuperinterface(className())
+ .addMethod(MethodSpec.methodBuilder(SUBSCRIBE_METHOD)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(ParameterizedTypeName.get(ClassName.get(Subscriber.class),
+ WildcardTypeName.supertypeOf(responseType())),
+ SUBSCRIBER)
+ .addStatement("$L.onSubscribe(new $T($L))", SUBSCRIBER,
+ TypeName.get(EmptySubscription.class), SUBSCRIBER)
+ .build())
+ .build();
+ }
+}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpec.java
deleted file mode 100644
index 84a69d0237f9..000000000000
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpec.java
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package software.amazon.awssdk.codegen.poet.paginators;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import java.security.InvalidParameterException;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.lang.model.element.Modifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import software.amazon.awssdk.codegen.docs.PaginationDocs;
-import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
-import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
-import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
-import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
-import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
-import software.amazon.awssdk.codegen.poet.ClassSpec;
-import software.amazon.awssdk.codegen.poet.PoetExtensions;
-import software.amazon.awssdk.codegen.poet.PoetUtils;
-import software.amazon.awssdk.codegen.poet.model.TypeProvider;
-import software.amazon.awssdk.core.pagination.NextPageFetcher;
-import software.amazon.awssdk.core.pagination.PaginatedItemsIterable;
-import software.amazon.awssdk.core.pagination.PaginatedResponsesIterator;
-import software.amazon.awssdk.core.pagination.SdkIterable;
-
-/**
- * Java poet {@link ClassSpec} to generate the response class for sync paginated operations.
- */
-public class PaginatorResponseClassSpec implements ClassSpec {
-
- private static final Logger log = LoggerFactory.getLogger(PaginatorResponseClassSpec.class);
-
- private static final String CLIENT_MEMBER = "client";
- private static final String REQUEST_MEMBER = "firstRequest";
- private static final String NEXT_PAGE_FETCHER_MEMBER = "nextPageFetcher";
- private static final String HAS_NEXT_PAGE_METHOD = "hasNextPage";
- private static final String NEXT_PAGE_METHOD = "nextPage";
- private static final String PREVIOUS_PAGE_METHOD_ARGUMENT = "previousPage";
-
- private final IntermediateModel model;
- private final PoetExtensions poetExtensions;
- private final TypeProvider typeProvider;
- private final String c2jOperationName;
- private final PaginatorDefinition paginatorDefinition;
- private final OperationModel operationModel;
- private final PaginationDocs paginationDocs;
-
- public PaginatorResponseClassSpec(IntermediateModel intermediateModel,
- String c2jOperationName,
- PaginatorDefinition paginatorDefinition) {
- this.model = intermediateModel;
- this.poetExtensions = new PoetExtensions(intermediateModel);
- this.typeProvider = new TypeProvider(intermediateModel);
- this.c2jOperationName = c2jOperationName;
- this.paginatorDefinition = paginatorDefinition;
- this.operationModel = model.getOperation(c2jOperationName);
- this.paginationDocs = new PaginationDocs(intermediateModel, operationModel);
- }
-
- @Override
- public TypeSpec poetSpec() {
- TypeSpec.Builder specBuilder = TypeSpec.classBuilder(className())
- .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
- .addAnnotation(PoetUtils.GENERATED)
- .addSuperinterface(getPaginatedResponseInterface())
- .addFields(Stream.of(syncClientInterfaceField(),
- requestClassField(),
- nextPageSupplierField())
- .collect(Collectors.toList()))
- .addMethod(constructor())
- .addMethod(iteratorMethod())
- .addMethods(getMethodSpecsForResultKeyList())
- .addJavadoc(paginationDocs.getDocsForSyncResponseClass(
- getClientInterfaceName()))
- .addType(nextPageFetcherClass());
-
- return specBuilder.build();
- }
-
- @Override
- public ClassName className() {
- return poetExtensions.getResponseClassForPaginatedSyncOperation(c2jOperationName);
- }
-
- /**
- * Returns the interface that is implemented by the Paginated Response class.
- */
- private TypeName getPaginatedResponseInterface() {
- return ParameterizedTypeName.get(ClassName.get(SdkIterable.class), responseType());
- }
-
- /**
- * @return A Poet {@link ClassName} for the sync operation request type.
- *
- * Example: For ListTables operation, it will be "ListTablesRequest" class.
- */
- private ClassName requestType() {
- return poetExtensions.getModelClass(operationModel.getInput().getVariableType());
- }
-
- /**
- * @return A Poet {@link ClassName} for the sync operation response type.
- *
- * Example: For ListTables operation, it will be "ListTablesResponse" class.
- */
- private ClassName responseType() {
- return poetExtensions.getModelClass(operationModel.getReturnType().getReturnType());
- }
-
- /**
- * @return A Poet {@link ClassName} for the sync client interface
- */
- private ClassName getClientInterfaceName() {
- return poetExtensions.getClientClass(model.getMetadata().getSyncInterface());
- }
-
- private FieldSpec syncClientInterfaceField() {
- return FieldSpec.builder(getClientInterfaceName(), CLIENT_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
- }
-
- private FieldSpec requestClassField() {
- return FieldSpec.builder(requestType(), REQUEST_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
- }
-
- private FieldSpec nextPageSupplierField() {
- return FieldSpec.builder(NextPageFetcher.class, NEXT_PAGE_FETCHER_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
- }
-
- private String nextPageFetcherClassName() {
- return operationModel.getReturnType().getReturnType() + "Fetcher";
- }
-
- private MethodSpec constructor() {
- return MethodSpec.constructorBuilder()
- .addModifiers(Modifier.PUBLIC)
- .addParameter(getClientInterfaceName(), CLIENT_MEMBER, Modifier.FINAL)
- .addParameter(requestType(), REQUEST_MEMBER, Modifier.FINAL)
- .addStatement("this.$L = $L", CLIENT_MEMBER, CLIENT_MEMBER)
- .addStatement("this.$L = $L", REQUEST_MEMBER, REQUEST_MEMBER)
- .addStatement("this.$L = new $L()", NEXT_PAGE_FETCHER_MEMBER, nextPageFetcherClassName())
- .build();
- }
-
- /**
- * A {@link MethodSpec} for the overridden iterator() method which is inherited
- * from the interface.
- */
- private MethodSpec iteratorMethod() {
- return MethodSpec.methodBuilder("iterator")
- .addAnnotation(Override.class)
- .addModifiers(Modifier.PUBLIC)
- .returns(ParameterizedTypeName.get(ClassName.get(Iterator.class), responseType()))
- .addStatement("return new $T($L)", PaginatedResponsesIterator.class, NEXT_PAGE_FETCHER_MEMBER)
- .build();
- }
-
- /**
- * Returns iterable of {@link MethodSpec} to generate helper methods for all members
- * in {@link PaginatorDefinition#getResultKey()}. All the generated methods return an SdkIterable.
- *
- * The helper methods to iterate on paginated member will be generated only
- * if {@link PaginatorDefinition#getResultKey()} is not null and a non-empty list.
- */
- private Iterable getMethodSpecsForResultKeyList() {
- if (paginatorDefinition.getResultKey() != null) {
- return paginatorDefinition.getResultKey().stream()
- .map(this::getMethodsSpecForSingleResultKey)
- .collect(Collectors.toList());
- }
- return Collections.emptyList();
- }
-
- /*
- * Generate a method spec for single element in {@link PaginatorDefinition#getResultKey()} list.
- *
- * If the element is "Folders" and its type is "List", generated code looks like:
- *
- * public SdkIterable folders() {
- * Function> getPaginatedMemberIterator =
- * response -> response != null ? response.folders().iterator() : null;
- *
- * return new PaginatedItemsIterable(this, getPaginatedMemberIterator);
- * }
- */
- private MethodSpec getMethodsSpecForSingleResultKey(String resultKey) {
- TypeName resultKeyType = getTypeForResultKey(resultKey);
- MemberModel resultKeyModel = memberModelForResponseMember(resultKey);
-
- return MethodSpec.methodBuilder(resultKeyModel.getFluentGetterMethodName())
- .addModifiers(Modifier.PUBLIC)
- .returns(ParameterizedTypeName.get(ClassName.get(SdkIterable.class), resultKeyType))
- .addCode("$T getIterator = ",
- ParameterizedTypeName.get(ClassName.get(Function.class),
- responseType(),
- ParameterizedTypeName.get(ClassName.get(Iterator.class),
- resultKeyType)))
- .addCode(getPaginatedMemberIteratorLambdaBlock(resultKey, resultKeyModel))
- .addCode("\n")
- .addStatement("return new $T(this, getIterator)", PaginatedItemsIterable.class)
- .addJavadoc(CodeBlock.builder()
- .add("Returns an iterable to iterate through the paginated {@link $T#$L()} member. "
- + "The returned iterable is used to iterate through the results across all "
- + "response pages and not a single page.\n",
- responseType(), resultKeyModel.getFluentGetterMethodName())
- .add("\n")
- .add("This method is useful if you are interested in iterating over the paginated "
- + "member in the response pages instead of the top level pages. "
- + "Similar to iteration over pages, this method internally makes service "
- + "calls to get the next list of results until the iteration stops or "
- + "there are no more results.")
- .build())
- .build();
- }
-
- private CodeBlock getPaginatedMemberIteratorLambdaBlock(String resultKey, MemberModel resultKeyModel) {
- final String response = "response";
- final String fluentGetter = fluentGetterMethodForResponseMember(resultKey);
-
- CodeBlock iteratorBlock = null;
-
- if (resultKeyModel.isList()) {
- iteratorBlock = CodeBlock.builder().add("$L.$L.iterator()", response, fluentGetter).build();
-
- } else if (resultKeyModel.isMap()) {
- iteratorBlock = CodeBlock.builder().add("$L.$L.entrySet().iterator()", response, fluentGetter).build();
- }
-
- return CodeBlock.builder()
- .addStatement("$L -> $L != null ? $L : null", response, response, iteratorBlock)
- .build();
- }
-
- /**
- * Returns a list of fluent setter method names for members in {@link PaginatorDefinition#getInputToken()} list.
- * The size of list returned by this method is equal to the size of {@link PaginatorDefinition#getInputToken()} list.
- */
- private List fluentSetterMethodNamesForInputToken() {
- return paginatorDefinition.getInputToken().stream()
- .map(this::fluentSetterNameForSingleInputToken)
- .collect(Collectors.toList());
- }
-
- /**
- * Returns the fluent setter method name for a single member in the request.
- *
- * The values in {@link PaginatorDefinition#getInputToken()} are not nested unlike
- * {@link PaginatorDefinition#getOutputToken()}.
- */
- private String fluentSetterNameForSingleInputToken(String inputToken) {
- return operationModel.getInputShape()
- .findMemberModelByC2jName(inputToken)
- .getFluentSetterMethodName();
- }
-
- /**
- * Returns a list of fluent getter methods for members in {@link PaginatorDefinition#getOutputToken()} list.
- * The size of list returned by this method is equal to the size of {@link PaginatorDefinition#getOutputToken()} list.
- */
- private List fluentGetterMethodsForOutputToken() {
- return paginatorDefinition.getOutputToken().stream()
- .map(this::fluentGetterMethodForResponseMember)
- .collect(Collectors.toList());
- }
-
- /**
- * Returns the fluent getter method for a single member in the response.
- * The returned String includes the '()' after each method name.
- *
- * The input member can be a nested String. An example would be StreamDescription.LastEvaluatedShardId
- * which represents LastEvaluatedShardId member in StreamDescription class. The return value for it
- * would be "streamDescription().lastEvaluatedShardId()"
- *
- * @param member A top level or nested member in response of {@link #c2jOperationName}.
- */
- private String fluentGetterMethodForResponseMember(String member) {
- final String[] hierarchy = member.split("\\.");
-
- if (hierarchy.length < 1) {
- throw new IllegalArgumentException(String.format("Error when splitting member %s for operation %s",
- member, c2jOperationName));
- }
-
- ShapeModel parentShape = operationModel.getOutputShape();
- final StringBuilder getterMethod = new StringBuilder();
-
- for (String str : hierarchy) {
- getterMethod.append(".")
- .append(parentShape.findMemberModelByC2jName(str).getFluentGetterMethodName())
- .append("()");
-
- parentShape = parentShape.findMemberModelByC2jName(str).getShape();
- }
-
- return getterMethod.substring(1);
- }
-
- /**
- * @param input A top level or nested member in response of {@link #c2jOperationName}.
- *
- * @return The {@link MemberModel} of the {@link PaginatorDefinition#getResultKey()}. If input value is nested,
- * then member model of the last child shape is returned.
- *
- * For example, if input is StreamDescription.Shards, then the return value is "Shard" which is the member model for
- * the Shards.
- */
- private MemberModel memberModelForResponseMember(String input) {
- final String[] hierarchy = input.split("\\.");
-
- if (hierarchy.length < 1) {
- throw new IllegalArgumentException(String.format("Error when splitting value %s for operation %s",
- input, c2jOperationName));
- }
-
- ShapeModel shape = operationModel.getOutputShape();
-
- for (int i = 0; i < hierarchy.length - 1; i++) {
- shape = shape.findMemberModelByC2jName(hierarchy[i]).getShape();
- }
-
- return shape.getMemberByC2jName(hierarchy[hierarchy.length - 1]);
- }
-
- /*
- * Returns the {@link TypeName} for a value in the {@link PaginatorDefinition#getResultKey()} list.
- *
- * Examples:
- * If paginated item is represented as List, then member type is String.
- * If paginated item is represented as List, then member type is Foo.
- * If paginated item is represented as Map>,
- * then member type is Map.Entry>.
- */
- private TypeName getTypeForResultKey(String singleResultKey) {
- MemberModel resultKeyModel = memberModelForResponseMember(singleResultKey);
-
- if (resultKeyModel == null) {
- throw new InvalidParameterException("MemberModel is not found for result key: " + singleResultKey);
- }
-
- if (resultKeyModel.isList()) {
- return typeProvider.fieldType(resultKeyModel.getListModel().getListMemberModel());
- } else if (resultKeyModel.isMap()) {
- return typeProvider.mapEntryWithConcreteTypes(resultKeyModel.getMapModel());
- } else {
- throw new IllegalArgumentException(String.format("Key %s in paginated operation %s should be either a list or a map",
- singleResultKey, c2jOperationName));
- }
- }
-
- /**
- * Generates a inner class that implements {@link NextPageFetcher}. An instance of this class
- * is passed to {@link PaginatedResponsesIterator} to be used while iterating through pages.
- */
- private TypeSpec nextPageFetcherClass() {
- return TypeSpec.classBuilder(nextPageFetcherClassName())
- .addModifiers(Modifier.PRIVATE)
- .addSuperinterface(ParameterizedTypeName.get(ClassName.get(NextPageFetcher.class), responseType()))
- .addMethod(MethodSpec.methodBuilder(HAS_NEXT_PAGE_METHOD)
- .addModifiers(Modifier.PUBLIC)
- .addAnnotation(Override.class)
- .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT)
- .returns(boolean.class)
- .addStatement(hasNextPageMethodBody())
- .build())
- .addMethod(MethodSpec.methodBuilder(NEXT_PAGE_METHOD)
- .addModifiers(Modifier.PUBLIC)
- .addAnnotation(Override.class)
- .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT)
- .returns(responseType())
- .addCode(nextPageMethodBody())
- .build())
- .build();
- }
-
- private String hasNextPageMethodBody() {
- String body;
-
- if (paginatorDefinition.getMoreResults() != null) {
- body = String.format("return %s.%s.booleanValue()",
- PREVIOUS_PAGE_METHOD_ARGUMENT,
- fluentGetterMethodForResponseMember(paginatorDefinition.getMoreResults()));
- } else {
- // If there is no more_results token, then output_token will be a single value
- body = String.format("return %s.%s != null",
- PREVIOUS_PAGE_METHOD_ARGUMENT,
- fluentGetterMethodsForOutputToken().get(0));
- }
-
- return body;
- }
-
- /*
- * Returns {@link CodeBlock} for the NEXT_PAGE_METHOD.
- *
- * A sample from dynamoDB listTables paginator:
- *
- * if (oldPage == null) {
- * return client.listTables(firstRequest);
- * } else {
- * return client.listTables(firstRequest.toBuilder().exclusiveStartTableName(response.lastEvaluatedTableName())
- * .build());
- * }
- */
- private CodeBlock nextPageMethodBody() {
- return CodeBlock.builder()
- .beginControlFlow("if ($L == null)", PREVIOUS_PAGE_METHOD_ARGUMENT)
- .addStatement("return $L.$L($L)", CLIENT_MEMBER, operationModel.getMethodName(), REQUEST_MEMBER)
- .endControlFlow()
- .addStatement(codeToGetNextPageIfOldResponseIsNotNull())
- .build();
- }
-
- /**
- * Generates the code to get next page by using values from old page.
- *
- * Sample generated code:
- * return client.listTables(firstRequest.toBuilder().exclusiveStartTableName(response.lastEvaluatedTableName()).build());
- */
- private String codeToGetNextPageIfOldResponseIsNotNull() {
- StringBuilder sb = new StringBuilder();
-
- sb.append(String.format("return %s.%s(%s.toBuilder()", CLIENT_MEMBER, operationModel.getMethodName(), REQUEST_MEMBER));
-
- List requestSetterNames = fluentSetterMethodNamesForInputToken();
- List responseGetterMethods = fluentGetterMethodsForOutputToken();
-
- for (int i = 0; i < paginatorDefinition.getInputToken().size(); i++) {
- sb.append(String.format(".%s(%s.%s)", requestSetterNames.get(i), PREVIOUS_PAGE_METHOD_ARGUMENT,
- responseGetterMethods.get(i)));
- }
-
- sb.append(".build())");
-
- return sb.toString();
- }
-}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorsClassSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorsClassSpec.java
new file mode 100644
index 000000000000..2e6f5cb4751d
--- /dev/null
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorsClassSpec.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.codegen.poet.paginators;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import java.security.InvalidParameterException;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Modifier;
+import software.amazon.awssdk.codegen.docs.PaginationDocs;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
+import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
+import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
+import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
+import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
+import software.amazon.awssdk.codegen.poet.ClassSpec;
+import software.amazon.awssdk.codegen.poet.PoetExtensions;
+import software.amazon.awssdk.codegen.poet.model.TypeProvider;
+
+public abstract class PaginatorsClassSpec implements ClassSpec {
+
+ protected static final String CLIENT_MEMBER = "client";
+ protected static final String REQUEST_MEMBER = "firstRequest";
+ protected static final String NEXT_PAGE_FETCHER_MEMBER = "nextPageFetcher";
+ protected static final String HAS_NEXT_PAGE_METHOD = "hasNextPage";
+ protected static final String NEXT_PAGE_METHOD = "nextPage";
+ protected static final String RESUME_METHOD = "resume";
+ protected static final String PREVIOUS_PAGE_METHOD_ARGUMENT = "previousPage";
+ protected static final String RESPONSE_LITERAL = "response";
+ protected static final String LAST_SUCCESSFUL_PAGE_LITERAL = "lastSuccessfulPage";
+
+ protected final IntermediateModel model;
+ protected final String c2jOperationName;
+ protected final PaginatorDefinition paginatorDefinition;
+ protected final PoetExtensions poetExtensions;
+ protected final TypeProvider typeProvider;
+ protected final OperationModel operationModel;
+ protected final PaginationDocs paginationDocs;
+
+ public PaginatorsClassSpec(IntermediateModel model, String c2jOperationName, PaginatorDefinition paginatorDefinition) {
+ this.model = model;
+ this.c2jOperationName = c2jOperationName;
+ this.paginatorDefinition = paginatorDefinition;
+ this.poetExtensions = new PoetExtensions(model);
+ this.typeProvider = new TypeProvider(model);
+ this.operationModel = model.getOperation(c2jOperationName);
+ this.paginationDocs = new PaginationDocs(model, operationModel);
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the operation request type.
+ *
+ * Example: For ListTables operation, it will be "ListTablesRequest" class.
+ */
+ protected ClassName requestType() {
+ return poetExtensions.getModelClass(operationModel.getInput().getVariableType());
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the sync operation response type.
+ *
+ * Example: For ListTables operation, it will be "ListTablesResponse" class.
+ */
+ protected ClassName responseType() {
+ return poetExtensions.getModelClass(operationModel.getReturnType().getReturnType());
+ }
+
+ // Generates
+ // private final ListTablesRequest firstRequest;
+ protected FieldSpec requestClassField() {
+ return FieldSpec.builder(requestType(), REQUEST_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
+ }
+
+ protected String nextPageFetcherClassName() {
+ return operationModel.getReturnType().getReturnType() + "Fetcher";
+ }
+
+ protected MethodSpec.Builder resumeMethodBuilder() {
+ return MethodSpec.methodBuilder(RESUME_METHOD)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addParameter(responseType(), LAST_SUCCESSFUL_PAGE_LITERAL, Modifier.FINAL)
+ .returns(className())
+ .addCode(CodeBlock.builder()
+ .beginControlFlow("if ($L.$L($L))", NEXT_PAGE_FETCHER_MEMBER,
+ HAS_NEXT_PAGE_METHOD, LAST_SUCCESSFUL_PAGE_LITERAL)
+ .addStatement("return new $T($L, $L)", className(), CLIENT_MEMBER,
+ constructRequestFromLastPage(LAST_SUCCESSFUL_PAGE_LITERAL))
+ .endControlFlow()
+ .build())
+ .addJavadoc(CodeBlock.builder()
+ .add("A helper method to resume the pages in case of unexpected failures. "
+ + "The method takes the last successful response page as input and returns an "
+ + "instance of {@link $T} that can be used to retrieve the consecutive pages "
+ + "that follows the input page.
", className())
+ .build());
+ }
+
+ /*
+ * Returns the {@link TypeName} for a value in the {@link PaginatorDefinition#getResultKey()} list.
+ *
+ * Examples:
+ * If paginated item is represented as List, then member type is String.
+ * If paginated item is represented as List, then member type is Foo.
+ * If paginated item is represented as Map>,
+ * then member type is Map.Entry>.
+ */
+ protected TypeName getTypeForResultKey(String singleResultKey) {
+ MemberModel resultKeyModel = memberModelForResponseMember(singleResultKey);
+
+ if (resultKeyModel == null) {
+ throw new InvalidParameterException("MemberModel is not found for result key: " + singleResultKey);
+ }
+
+ if (resultKeyModel.isList()) {
+ return typeProvider.fieldType(resultKeyModel.getListModel().getListMemberModel());
+ } else if (resultKeyModel.isMap()) {
+ return typeProvider.mapEntryWithConcreteTypes(resultKeyModel.getMapModel());
+ } else {
+ throw new IllegalArgumentException(String.format("Key %s in paginated operation %s should be either a list or a map",
+ singleResultKey, c2jOperationName));
+ }
+ }
+
+ /**
+ * @param input A top level or nested member in response of {@link #c2jOperationName}.
+ *
+ * @return The {@link MemberModel} of the {@link PaginatorDefinition#getResultKey()}. If input value is nested,
+ * then member model of the last child shape is returned.
+ *
+ * For example, if input is StreamDescription.Shards, then the return value is "Shard" which is the member model for
+ * the Shards.
+ */
+ protected MemberModel memberModelForResponseMember(String input) {
+ final String[] hierarchy = input.split("\\.");
+
+ if (hierarchy.length < 1) {
+ throw new IllegalArgumentException(String.format("Error when splitting value %s for operation %s",
+ input, c2jOperationName));
+ }
+
+ ShapeModel shape = operationModel.getOutputShape();
+
+ for (int i = 0; i < hierarchy.length - 1; i++) {
+ shape = shape.findMemberModelByC2jName(hierarchy[i]).getShape();
+ }
+
+ return shape.getMemberByC2jName(hierarchy[hierarchy.length - 1]);
+ }
+
+ protected String hasNextPageMethodBody() {
+ String body;
+
+ if (paginatorDefinition.getMoreResults() != null) {
+ body = String.format("return %s.%s.booleanValue()",
+ PREVIOUS_PAGE_METHOD_ARGUMENT,
+ fluentGetterMethodForResponseMember(paginatorDefinition.getMoreResults()));
+ } else {
+ // If there is no more_results token, then output_token will be a single value
+ body = String.format("return %s.%s != null",
+ PREVIOUS_PAGE_METHOD_ARGUMENT,
+ fluentGetterMethodsForOutputToken().get(0));
+ }
+
+ return body;
+ }
+
+ /*
+ * Returns {@link CodeBlock} for the NEXT_PAGE_METHOD.
+ *
+ * A sample from dynamoDB listTables paginator:
+ *
+ * if (oldPage == null) {
+ * return client.listTables(firstRequest);
+ * } else {
+ * return client.listTables(firstRequest.toBuilder().exclusiveStartTableName(response.lastEvaluatedTableName())
+ * .build());
+ * }
+ */
+ protected CodeBlock nextPageMethodBody() {
+ return CodeBlock.builder()
+ .beginControlFlow("if ($L == null)", PREVIOUS_PAGE_METHOD_ARGUMENT)
+ .addStatement("return $L.$L($L)", CLIENT_MEMBER, operationModel.getMethodName(), REQUEST_MEMBER)
+ .endControlFlow()
+ .addStatement(codeToGetNextPageIfOldResponseIsNotNull())
+ .build();
+ }
+
+ /**
+ * Generates the code to get next page by using values from old page.
+ *
+ * Sample generated code:
+ * return client.listTables(firstRequest.toBuilder().exclusiveStartTableName(response.lastEvaluatedTableName()).build());
+ */
+ private String codeToGetNextPageIfOldResponseIsNotNull() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("return %s.%s(%s)", CLIENT_MEMBER,
+ operationModel.getMethodName(),
+ constructRequestFromLastPage(PREVIOUS_PAGE_METHOD_ARGUMENT)));
+ return sb.toString();
+ }
+
+ /**
+ * Generates the code to construct a request object from the last successful page
+ * by setting the fields required to get the next page.
+ *
+ * Sample code: if responsePage string is "response"
+ * firstRequest.toBuilder().exclusiveStartTableName(response.lastEvaluatedTableName()).build()
+ */
+ protected String constructRequestFromLastPage(String responsePage) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("%s.toBuilder()", REQUEST_MEMBER));
+
+ List requestSetterNames = fluentSetterMethodNamesForInputToken();
+ List responseGetterMethods = fluentGetterMethodsForOutputToken();
+
+ for (int i = 0; i < paginatorDefinition.getInputToken().size(); i++) {
+ sb.append(String.format(".%s(%s.%s)", requestSetterNames.get(i), responsePage,
+ responseGetterMethods.get(i)));
+ }
+
+ sb.append(".build()");
+ return sb.toString();
+ }
+
+ /**
+ * Returns a list of fluent setter method names for members in {@link PaginatorDefinition#getInputToken()} list.
+ * The size of list returned by this method is equal to the size of {@link PaginatorDefinition#getInputToken()} list.
+ */
+ private List fluentSetterMethodNamesForInputToken() {
+ return paginatorDefinition.getInputToken().stream()
+ .map(this::fluentSetterNameForSingleInputToken)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the fluent setter method name for a single member in the request.
+ *
+ * The values in {@link PaginatorDefinition#getInputToken()} are not nested unlike
+ * {@link PaginatorDefinition#getOutputToken()}.
+ */
+ private String fluentSetterNameForSingleInputToken(String inputToken) {
+ return operationModel.getInputShape()
+ .findMemberModelByC2jName(inputToken)
+ .getFluentSetterMethodName();
+ }
+
+ /**
+ * Returns a list of fluent getter methods for members in {@link PaginatorDefinition#getOutputToken()} list.
+ * The size of list returned by this method is equal to the size of {@link PaginatorDefinition#getOutputToken()} list.
+ */
+ private List fluentGetterMethodsForOutputToken() {
+ return paginatorDefinition.getOutputToken().stream()
+ .map(this::fluentGetterMethodForResponseMember)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the fluent getter method for a single member in the response.
+ * The returned String includes the '()' after each method name.
+ *
+ * The input member can be a nested String. An example would be StreamDescription.LastEvaluatedShardId
+ * which represents LastEvaluatedShardId member in StreamDescription class. The return value for it
+ * would be "streamDescription().lastEvaluatedShardId()"
+ *
+ * @param member A top level or nested member in response of {@link #c2jOperationName}.
+ */
+ private String fluentGetterMethodForResponseMember(String member) {
+ final String[] hierarchy = member.split("\\.");
+
+ if (hierarchy.length < 1) {
+ throw new IllegalArgumentException(String.format("Error when splitting member %s for operation %s",
+ member, c2jOperationName));
+ }
+
+ ShapeModel parentShape = operationModel.getOutputShape();
+ final StringBuilder getterMethod = new StringBuilder();
+
+ for (String str : hierarchy) {
+ getterMethod.append(".")
+ .append(parentShape.findMemberModelByC2jName(str).getFluentGetterMethodName())
+ .append("()");
+
+ parentShape = parentShape.findMemberModelByC2jName(str).getShape();
+ }
+
+ return getterMethod.substring(1);
+ }
+
+ protected CodeBlock getIteratorLambdaBlock(String resultKey, MemberModel resultKeyModel) {
+ final String conditionalStatement = getConditionalStatementforIteratorLambda(resultKey);
+ final String fluentGetter = fluentGetterMethodForResponseMember(resultKey);
+
+ CodeBlock iteratorBlock = null;
+ if (resultKeyModel.isList()) {
+ iteratorBlock = CodeBlock.builder().addStatement("return $L.$L.iterator()", RESPONSE_LITERAL, fluentGetter).build();
+
+ } else if (resultKeyModel.isMap()) {
+ iteratorBlock = CodeBlock.builder().addStatement("return $L.$L.entrySet().iterator()",
+ RESPONSE_LITERAL,
+ fluentGetter).build();
+ }
+
+ CodeBlock conditionalBlock = CodeBlock.builder()
+ .beginControlFlow("if ($L)", conditionalStatement)
+ .add(iteratorBlock)
+ .endControlFlow()
+ .addStatement("return $T.emptyIterator()", TypeName.get(Collections.class))
+ .build();
+
+ return CodeBlock.builder()
+ .add("$L -> { $L };", RESPONSE_LITERAL, conditionalBlock)
+ .build();
+ }
+
+ /**
+ * Returns a conditional statement string that verifies the fluent methods to return result key are not null.
+ *
+ * If resultKey is StreamDescription.LastEvaluatedShardId, output of this method would be
+ * "response != null && response.streamDescription() != null && response.streamDescription().lastEvaluatedShardId() != null"
+ *
+ * @param resultKey A top level or nested member in response of {@link #c2jOperationName}.
+ */
+ private String getConditionalStatementforIteratorLambda(String resultKey) {
+ final String[] hierarchy = resultKey.split("\\.");
+
+ if (hierarchy.length < 1) {
+ throw new IllegalArgumentException(String.format("Error when splitting member %s for operation %s",
+ resultKey, c2jOperationName));
+ }
+
+ String currentFluentMethod = RESPONSE_LITERAL;
+ ShapeModel parentShape = operationModel.getOutputShape();
+
+ final StringBuilder conditionStatement = new StringBuilder(String.format("%s != null", currentFluentMethod));
+
+ for (String str : hierarchy) {
+ currentFluentMethod = String.format("%s.%s()", currentFluentMethod, parentShape.findMemberModelByC2jName(str)
+ .getFluentGetterMethodName());
+ conditionStatement.append(" && ");
+ conditionStatement.append(String.format("%s != null", currentFluentMethod));
+
+ parentShape = parentShape.findMemberModelByC2jName(str).getShape();
+ }
+
+ return conditionStatement.toString();
+ }
+}
\ No newline at end of file
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/SyncResponseClassSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/SyncResponseClassSpec.java
new file mode 100644
index 000000000000..a0d450d4e41e
--- /dev/null
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/paginators/SyncResponseClassSpec.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.codegen.poet.paginators;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.lang.model.element.Modifier;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
+import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
+import software.amazon.awssdk.codegen.model.service.PaginatorDefinition;
+import software.amazon.awssdk.codegen.poet.ClassSpec;
+import software.amazon.awssdk.codegen.poet.PoetUtils;
+import software.amazon.awssdk.core.pagination.PaginatedItemsIterable;
+import software.amazon.awssdk.core.pagination.PaginatedResponsesIterator;
+import software.amazon.awssdk.core.pagination.SdkIterable;
+import software.amazon.awssdk.core.pagination.SyncPageFetcher;
+
+/**
+ * Java poet {@link ClassSpec} to generate the response class for sync paginated operations.
+ */
+public class SyncResponseClassSpec extends PaginatorsClassSpec {
+
+ private static final String ITERATOR_METHOD = "iterator";
+
+ public SyncResponseClassSpec(IntermediateModel model, String c2jOperationName, PaginatorDefinition paginatorDefinition) {
+ super(model, c2jOperationName, paginatorDefinition);
+ }
+
+ @Override
+ public TypeSpec poetSpec() {
+ TypeSpec.Builder specBuilder = TypeSpec.classBuilder(className())
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(PoetUtils.GENERATED)
+ .addSuperinterface(getSyncResponseInterface())
+ .addFields(Stream.of(syncClientInterfaceField(),
+ requestClassField(),
+ syncPageFetcherField())
+ .collect(Collectors.toList()))
+ .addMethod(constructor())
+ .addMethod(iteratorMethod())
+ .addMethods(getMethodSpecsForResultKeyList())
+ .addMethod(resumeMethod())
+ .addJavadoc(paginationDocs.getDocsForSyncResponseClass(
+ getClientInterfaceName()))
+ .addType(nextPageFetcherClass());
+
+ return specBuilder.build();
+ }
+
+ @Override
+ public ClassName className() {
+ return poetExtensions.getResponseClassForPaginatedSyncOperation(c2jOperationName);
+ }
+
+ /**
+ * Returns the interface that is implemented by the Paginated Sync Response class.
+ */
+ private TypeName getSyncResponseInterface() {
+ return ParameterizedTypeName.get(ClassName.get(SdkIterable.class), responseType());
+ }
+
+ /**
+ * @return A Poet {@link ClassName} for the sync client interface
+ */
+ private ClassName getClientInterfaceName() {
+ return poetExtensions.getClientClass(model.getMetadata().getSyncInterface());
+ }
+
+ private FieldSpec syncClientInterfaceField() {
+ return FieldSpec.builder(getClientInterfaceName(), CLIENT_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
+ }
+
+ private FieldSpec syncPageFetcherField() {
+ return FieldSpec.builder(SyncPageFetcher.class, NEXT_PAGE_FETCHER_MEMBER, Modifier.PRIVATE, Modifier.FINAL).build();
+ }
+
+ private MethodSpec constructor() {
+ return MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(getClientInterfaceName(), CLIENT_MEMBER, Modifier.FINAL)
+ .addParameter(requestType(), REQUEST_MEMBER, Modifier.FINAL)
+ .addStatement("this.$L = $L", CLIENT_MEMBER, CLIENT_MEMBER)
+ .addStatement("this.$L = $L", REQUEST_MEMBER, REQUEST_MEMBER)
+ .addStatement("this.$L = new $L()", NEXT_PAGE_FETCHER_MEMBER, nextPageFetcherClassName())
+ .build();
+ }
+
+ /**
+ * A {@link MethodSpec} for the overridden iterator() method which is inherited
+ * from the interface.
+ */
+ private MethodSpec iteratorMethod() {
+ return MethodSpec.methodBuilder(ITERATOR_METHOD)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(ParameterizedTypeName.get(ClassName.get(Iterator.class), responseType()))
+ .addStatement("return new $T($L)", PaginatedResponsesIterator.class, NEXT_PAGE_FETCHER_MEMBER)
+ .build();
+ }
+
+ /**
+ * Returns iterable of {@link MethodSpec} to generate helper methods for all members
+ * in {@link PaginatorDefinition#getResultKey()}. All the generated methods return an SdkIterable.
+ *
+ * The helper methods to iterate on paginated member will be generated only
+ * if {@link PaginatorDefinition#getResultKey()} is not null and a non-empty list.
+ */
+ private Iterable getMethodSpecsForResultKeyList() {
+ if (paginatorDefinition.getResultKey() != null) {
+ return paginatorDefinition.getResultKey().stream()
+ .map(this::getMethodsSpecForSingleResultKey)
+ .collect(Collectors.toList());
+ }
+ return Collections.emptyList();
+ }
+
+ /*
+ * Generate a method spec for single element in {@link PaginatorDefinition#getResultKey()} list.
+ *
+ * If the element is "Folders" and its type is "List", generated code looks like:
+ *
+ * public SdkIterable folders() {
+ * Function> getIterator = response -> {
+ * if (response != null && response.folders() != null) {
+ * return response.folders().iterator();
+ * }
+ * return Collections.emptyIterator();
+ * };
+ * return new PaginatedItemsIterable(this, getIterator);
+ * }
+ */
+ private MethodSpec getMethodsSpecForSingleResultKey(String resultKey) {
+ TypeName resultKeyType = getTypeForResultKey(resultKey);
+ MemberModel resultKeyModel = memberModelForResponseMember(resultKey);
+
+ return MethodSpec.methodBuilder(resultKeyModel.getFluentGetterMethodName())
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(ParameterizedTypeName.get(ClassName.get(SdkIterable.class), resultKeyType))
+ .addCode("$T getIterator = ",
+ ParameterizedTypeName.get(ClassName.get(Function.class),
+ responseType(),
+ ParameterizedTypeName.get(ClassName.get(Iterator.class),
+ resultKeyType)))
+ .addCode(getIteratorLambdaBlock(resultKey, resultKeyModel))
+ .addCode("\n")
+ .addStatement("return new $T(this, getIterator)", PaginatedItemsIterable.class)
+ .addJavadoc(CodeBlock.builder()
+ .add("Returns an iterable to iterate through the paginated {@link $T#$L()} member."
+ + " The returned iterable is used to iterate through the results across all "
+ + "response pages and not a single page.\n",
+ responseType(), resultKeyModel.getFluentGetterMethodName())
+ .add("\n")
+ .add("This method is useful if you are interested in iterating over the paginated "
+ + "member in the response pages instead of the top level pages. "
+ + "Similar to iteration over pages, this method internally makes service "
+ + "calls to get the next list of results until the iteration stops or "
+ + "there are no more results.")
+ .build())
+ .build();
+ }
+
+ /**
+ * Generates a inner class that implements {@link SyncPageFetcher}. An instance of this class
+ * is passed to {@link PaginatedResponsesIterator} to be used while iterating through pages.
+ */
+ private TypeSpec nextPageFetcherClass() {
+ return TypeSpec.classBuilder(nextPageFetcherClassName())
+ .addModifiers(Modifier.PRIVATE)
+ .addSuperinterface(ParameterizedTypeName.get(ClassName.get(SyncPageFetcher.class), responseType()))
+ .addMethod(MethodSpec.methodBuilder(HAS_NEXT_PAGE_METHOD)
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT)
+ .returns(boolean.class)
+ .addStatement(hasNextPageMethodBody())
+ .build())
+ .addMethod(MethodSpec.methodBuilder(NEXT_PAGE_METHOD)
+ .addModifiers(Modifier.PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(responseType(), PREVIOUS_PAGE_METHOD_ARGUMENT)
+ .returns(responseType())
+ .addCode(nextPageMethodBody())
+ .build())
+ .build();
+ }
+
+ private MethodSpec resumeMethod() {
+ return resumeMethodBuilder().addStatement("return $L", anonymousClassWithEmptyIterator())
+ .build();
+ }
+
+ private TypeSpec anonymousClassWithEmptyIterator() {
+ return TypeSpec.anonymousClassBuilder("$L, $L", CLIENT_MEMBER, REQUEST_MEMBER)
+ .addSuperinterface(className())
+ .addMethod(MethodSpec.methodBuilder(ITERATOR_METHOD)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(ParameterizedTypeName.get(ClassName.get(Iterator.class), responseType()))
+ .addStatement("return $T.emptyIterator()", TypeName.get(Collections.class))
+ .build())
+ .build();
+ }
+}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/utils/PaginatorUtils.java b/codegen/src/main/java/software/amazon/awssdk/codegen/utils/PaginatorUtils.java
index 2e6648352a51..4e43e22d531e 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/utils/PaginatorUtils.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/utils/PaginatorUtils.java
@@ -24,10 +24,10 @@ private PaginatorUtils() {
/**
* @param methodName Name of a method in sync client
- * @return the name of the pagination enabled sync operation
+ * @return the name of the auto-pagination enabled operation
*/
@ReviewBeforeRelease("Naming of paginated APIs")
- public static String getSyncMethodName(String methodName) {
- return methodName + "Iterable";
+ public static String getPaginatedMethodName(String methodName) {
+ return methodName + "Paginator";
}
}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpecTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatedResponseClassSpecTest.java
similarity index 71%
rename from codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpecTest.java
rename to codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatedResponseClassSpecTest.java
index c03f8327e59d..974a1cc7bb5b 100644
--- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatorResponseClassSpecTest.java
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/paginators/PaginatedResponseClassSpecTest.java
@@ -31,17 +31,17 @@
import software.amazon.awssdk.codegen.poet.ClassSpec;
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils;
-public class PaginatorResponseClassSpecTest {
+public class PaginatedResponseClassSpecTest {
private static IntermediateModel intermediateModel;
private static Paginators paginators;
@BeforeClass
public static void setUp() throws IOException {
- File serviceModelFile = new File(PaginatorResponseClassSpecTest.class.getResource("service-2.json").getFile());
- File customizationConfigFile = new File(PaginatorResponseClassSpecTest.class.getResource("customization.config")
+ File serviceModelFile = new File(PaginatedResponseClassSpecTest.class.getResource("service-2.json").getFile());
+ File customizationConfigFile = new File(PaginatedResponseClassSpecTest.class.getResource("customization.config")
.getFile());
- File paginatorsModel = new File(PaginatorResponseClassSpecTest.class.getResource("paginators.json")
+ File paginatorsModel = new File(PaginatedResponseClassSpecTest.class.getResource("paginators.json")
.getFile());
paginators = getPaginatorsModel(paginatorsModel);
@@ -60,15 +60,29 @@ private static Paginators getPaginatorsModel(File file) {
}
@Test
- public void testGeneratedResponseClassesForPaginatedOperations() {
+ public void testGeneratedResponseForSyncOperations() {
paginators.getPaginators().entrySet()
.stream()
.filter(entry -> entry.getValue().isValid())
.forEach(entry ->
{
- ClassSpec classSpec = new PaginatorResponseClassSpec(intermediateModel,
- entry.getKey(),
- entry.getValue());
+ ClassSpec classSpec = new SyncResponseClassSpec(intermediateModel,
+ entry.getKey(),
+ entry.getValue());
+ assertThat(classSpec, generatesTo(classSpec.className().simpleName() + ".java"));
+ });
+ }
+
+ @Test
+ public void testGeneratedResponseForAsyncOperations() {
+ paginators.getPaginators().entrySet()
+ .stream()
+ .filter(entry -> entry.getValue().isValid())
+ .forEach(entry ->
+ {
+ ClassSpec classSpec = new AsyncResponseClassSpec(intermediateModel,
+ entry.getKey(),
+ entry.getValue());
assertThat(classSpec, generatesTo(classSpec.className().simpleName() + ".java"));
});
}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/customization.config b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/customization.config
index 1d9307c09d0f..1e2ac52e4f2f 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/customization.config
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/customization.config
@@ -4,5 +4,6 @@
},
"presignersFqcn": "software.amazon.awssdk.services.acm.presign.AcmClientPresigners",
"serviceSpecificHttpConfig": "MyServiceHttpConfig.CONFIG",
- "serviceSpecificClientConfigClass": "AdvancedConfiguration"
+ "serviceSpecificClientConfigClass": "AdvancedConfiguration",
+ "verifiedSimpleMethods" : ["paginatedOperationWithResultKey"]
}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/service-2.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/service-2.json
index 673a6ad2a685..0f58e416c3c1 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/service-2.json
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/json/service-2.json
@@ -237,9 +237,6 @@
},
"PaginatedOperationWithResultKeyRequest": {
"type": "structure",
- "required": [
- "NextToken"
- ],
"members": {
"NextToken": {
"shape": "subMember",
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java
index f05b9bcccd88..be0e7eb9661e 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java
@@ -31,6 +31,8 @@
import software.amazon.awssdk.services.json.model.StreamingInputOperationResponse;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationRequest;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationResponse;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher;
import software.amazon.awssdk.services.json.transform.APostOperationRequestMarshaller;
import software.amazon.awssdk.services.json.transform.APostOperationResponseUnmarshaller;
import software.amazon.awssdk.services.json.transform.APostOperationWithOutputRequestMarshaller;
@@ -95,14 +97,14 @@ public final String serviceName() {
public CompletableFuture aPostOperation(APostOperationRequest aPostOperationRequest) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new APostOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new APostOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler.execute(new ClientExecutionParams()
- .withMarshaller(new APostOperationRequestMarshaller(protocolFactory)).withResponseHandler(responseHandler)
- .withErrorResponseHandler(errorResponseHandler).withInput(aPostOperationRequest));
+ .withMarshaller(new APostOperationRequestMarshaller(protocolFactory)).withResponseHandler(responseHandler)
+ .withErrorResponseHandler(errorResponseHandler).withInput(aPostOperationRequest));
}
/**
@@ -130,19 +132,19 @@ public CompletableFuture aPostOperation(APostOperationRe
*/
@Override
public CompletableFuture aPostOperationWithOutput(
- APostOperationWithOutputRequest aPostOperationWithOutputRequest) {
+ APostOperationWithOutputRequest aPostOperationWithOutputRequest) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new APostOperationWithOutputResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new APostOperationWithOutputResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(aPostOperationWithOutputRequest));
+ .execute(new ClientExecutionParams()
+ .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(aPostOperationWithOutputRequest));
}
/**
@@ -170,19 +172,19 @@ public CompletableFuture aPostOperationWithOut
*/
@Override
public CompletableFuture getWithoutRequiredMembers(
- GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) {
+ GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new GetWithoutRequiredMembersResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new GetWithoutRequiredMembersResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withMarshaller(new GetWithoutRequiredMembersRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(getWithoutRequiredMembersRequest));
+ .execute(new ClientExecutionParams()
+ .withMarshaller(new GetWithoutRequiredMembersRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(getWithoutRequiredMembersRequest));
}
/**
@@ -207,19 +209,19 @@ public CompletableFuture getWithoutRequiredMe
*/
@Override
public CompletableFuture paginatedOperationWithResultKey(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new PaginatedOperationWithResultKeyResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new PaginatedOperationWithResultKeyResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withMarshaller(new PaginatedOperationWithResultKeyRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(paginatedOperationWithResultKeyRequest));
+ .execute(new ClientExecutionParams()
+ .withMarshaller(new PaginatedOperationWithResultKeyRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(paginatedOperationWithResultKeyRequest));
}
/**
@@ -244,19 +246,19 @@ public CompletableFuture paginatedOpera
*/
@Override
public CompletableFuture paginatedOperationWithoutResultKey(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new PaginatedOperationWithoutResultKeyResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new PaginatedOperationWithoutResultKeyResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withMarshaller(new PaginatedOperationWithoutResultKeyRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(paginatedOperationWithoutResultKeyRequest));
+ .execute(new ClientExecutionParams()
+ .withMarshaller(new PaginatedOperationWithoutResultKeyRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(paginatedOperationWithoutResultKeyRequest));
}
/**
@@ -286,18 +288,18 @@ public CompletableFuture paginatedOp
*/
@Override
public CompletableFuture streamingInputOperation(
- StreamingInputOperationRequest streamingInputOperationRequest, AsyncRequestProvider requestProvider) {
+ StreamingInputOperationRequest streamingInputOperationRequest, AsyncRequestProvider requestProvider) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new StreamingInputOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new StreamingInputOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler.execute(new ClientExecutionParams()
- .withMarshaller(new StreamingInputOperationRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withAsyncRequestProvider(requestProvider).withInput(streamingInputOperationRequest));
+ .withMarshaller(new StreamingInputOperationRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withAsyncRequestProvider(requestProvider).withInput(streamingInputOperationRequest));
}
/**
@@ -326,20 +328,166 @@ public CompletableFuture streamingInputOperatio
*/
@Override
public CompletableFuture streamingOutputOperation(
- StreamingOutputOperationRequest streamingOutputOperationRequest,
- AsyncResponseHandler asyncResponseHandler) {
+ StreamingOutputOperationRequest streamingOutputOperationRequest,
+ AsyncResponseHandler asyncResponseHandler) {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(false).withHasStreamingSuccessResponse(true),
- new StreamingOutputOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(false).withHasStreamingSuccessResponse(true),
+ new StreamingOutputOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler.execute(
- new ClientExecutionParams()
- .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory))
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(streamingOutputOperationRequest), asyncResponseHandler);
+ new ClientExecutionParams()
+ .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory))
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(streamingOutputOperationRequest), asyncResponseHandler);
+ }
+
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation. The return type is a custom publisher that can be subscribed to request a stream of response pages.
+ * SDK will internally handle making service calls for you.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet
+ * and so there is no guarantee that the request is valid. If there are errors in your request, you will see the
+ * failures only after you start streaming the data. The subscribe method should be called as a request to start
+ * streaming data. For more info, see
+ * {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the subscribe
+ * method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data from the
+ * starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @param paginatedOperationWithResultKeyRequest
+ * @return A custom publisher that can be subscribed to request a stream of response pages.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithResultKey
+ * @see AWS API Documentation
+ */
+ public PaginatedOperationWithResultKeyPublisher paginatedOperationWithResultKeyPaginator(
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
+ return new PaginatedOperationWithResultKeyPublisher(this, paginatedOperationWithResultKeyRequest);
+ }
+
+ /**
+ * Some paginated operation without result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithoutResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation. The return type is a custom publisher that can be subscribed to request a stream of response pages.
+ * SDK will internally handle making service calls for you.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet
+ * and so there is no guarantee that the request is valid. If there are errors in your request, you will see the
+ * failures only after you start streaming the data. The subscribe method should be called as a request to start
+ * streaming data. For more info, see
+ * {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the subscribe
+ * method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data from the
+ * starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithoutResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @param paginatedOperationWithoutResultKeyRequest
+ * @return A custom publisher that can be subscribed to request a stream of response pages.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithoutResultKey
+ * @see AWS API Documentation
+ */
+ public PaginatedOperationWithoutResultKeyPublisher paginatedOperationWithoutResultKeyPaginator(
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
+ return new PaginatedOperationWithoutResultKeyPublisher(this, paginatedOperationWithoutResultKeyRequest);
}
@Override
@@ -349,13 +497,13 @@ public void close() {
private software.amazon.awssdk.core.protocol.json.SdkJsonProtocolFactory init() {
return new SdkJsonProtocolFactory(new JsonClientMetadata()
- .withProtocolVersion("1.1")
- .withSupportsCbor(false)
- .withSupportsIon(false)
- .withBaseServiceExceptionClass(software.amazon.awssdk.services.json.model.JsonException.class)
- .withContentTypeOverride("")
- .addErrorMetadata(
- new JsonErrorShapeMetadata().withErrorCode("InvalidInput").withModeledClass(InvalidInputException.class)));
+ .withProtocolVersion("1.1")
+ .withSupportsCbor(false)
+ .withSupportsIon(false)
+ .withBaseServiceExceptionClass(software.amazon.awssdk.services.json.model.JsonException.class)
+ .withContentTypeOverride("")
+ .addErrorMetadata(
+ new JsonErrorShapeMetadata().withErrorCode("InvalidInput").withModeledClass(InvalidInputException.class)));
}
private HttpResponseHandler createErrorResponseHandler() {
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-async-client-interface.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-async-client-interface.java
index 4214500d0fab..97a265cad9d6 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-async-client-interface.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-async-client-interface.java
@@ -21,6 +21,8 @@
import software.amazon.awssdk.services.json.model.StreamingInputOperationResponse;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationRequest;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationResponse;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher;
import software.amazon.awssdk.utils.SdkAutoCloseable;
/**
@@ -131,7 +133,7 @@ default CompletableFuture aPostOperation(ConsumerAWS API Documentation
*/
default CompletableFuture aPostOperationWithOutput(
- APostOperationWithOutputRequest aPostOperationWithOutputRequest) {
+ APostOperationWithOutputRequest aPostOperationWithOutputRequest) {
throw new UnsupportedOperationException();
}
@@ -163,7 +165,7 @@ default CompletableFuture aPostOperationWithOu
* target="_top">AWS API Documentation
*/
default CompletableFuture aPostOperationWithOutput(
- Consumer aPostOperationWithOutputRequest) {
+ Consumer aPostOperationWithOutputRequest) {
return aPostOperationWithOutput(APostOperationWithOutputRequest.builder().apply(aPostOperationWithOutputRequest).build());
}
@@ -191,7 +193,7 @@ default CompletableFuture aPostOperationWithOu
* target="_top">AWS API Documentation
*/
default CompletableFuture getWithoutRequiredMembers(
- GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) {
+ GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) {
throw new UnsupportedOperationException();
}
@@ -249,9 +251,9 @@ default CompletableFuture getWithoutRequiredM
* target="_top">AWS API Documentation
*/
default CompletableFuture getWithoutRequiredMembers(
- Consumer getWithoutRequiredMembersRequest) {
+ Consumer getWithoutRequiredMembersRequest) {
return getWithoutRequiredMembers(GetWithoutRequiredMembersRequest.builder().apply(getWithoutRequiredMembersRequest)
- .build());
+ .build());
}
/**
@@ -275,10 +277,33 @@ default CompletableFuture getWithoutRequiredM
* target="_top">AWS API Documentation
*/
default CompletableFuture paginatedOperationWithResultKey(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
throw new UnsupportedOperationException();
}
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * @return A Java Future containing the result of the PaginatedOperationWithResultKey operation returned by the
+ * service.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithResultKey
+ * @see AWS API Documentation
+ */
+ default CompletableFuture paginatedOperationWithResultKey() {
+ return paginatedOperationWithResultKey(PaginatedOperationWithResultKeyRequest.builder().build());
+ }
+
/**
* Some paginated operation with result_key in paginators.json file
* This is a convenience which creates an instance of the {@link PaginatedOperationWithResultKeyRequest.Builder}
@@ -303,9 +328,153 @@ default CompletableFuture paginatedOper
* target="_top">AWS API Documentation
*/
default CompletableFuture paginatedOperationWithResultKey(
- Consumer paginatedOperationWithResultKeyRequest) {
+ Consumer paginatedOperationWithResultKeyRequest) {
return paginatedOperationWithResultKey(PaginatedOperationWithResultKeyRequest.builder()
- .apply(paginatedOperationWithResultKeyRequest).build());
+ .apply(paginatedOperationWithResultKeyRequest).build());
+ }
+
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation. The return type is a custom publisher that can be subscribed to request a stream of response pages.
+ * SDK will internally handle making service calls for you.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet
+ * and so there is no guarantee that the request is valid. If there are errors in your request, you will see the
+ * failures only after you start streaming the data. The subscribe method should be called as a request to start
+ * streaming data. For more info, see
+ * {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the subscribe
+ * method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data from the
+ * starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @param paginatedOperationWithResultKeyRequest
+ * @return A custom publisher that can be subscribed to request a stream of response pages.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithResultKey
+ * @see AWS API Documentation
+ */
+ default PaginatedOperationWithResultKeyPublisher paginatedOperationWithResultKeyPaginator(
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation. The return type is a custom publisher that can be subscribed to request a stream of response pages.
+ * SDK will internally handle making service calls for you.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet
+ * and so there is no guarantee that the request is valid. If there are errors in your request, you will see the
+ * failures only after you start streaming the data. The subscribe method should be called as a request to start
+ * streaming data. For more info, see
+ * {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the subscribe
+ * method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data from the
+ * starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @return A custom publisher that can be subscribed to request a stream of response pages.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithResultKey
+ * @see AWS API Documentation
+ */
+ default PaginatedOperationWithResultKeyPublisher paginatedOperationWithResultKeyPaginator() {
+ return paginatedOperationWithResultKeyPaginator(PaginatedOperationWithResultKeyRequest.builder().build());
}
/**
@@ -329,7 +498,7 @@ default CompletableFuture paginatedOper
* target="_top">AWS API Documentation
*/
default CompletableFuture paginatedOperationWithoutResultKey(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
throw new UnsupportedOperationException();
}
@@ -357,9 +526,82 @@ default CompletableFuture paginatedO
* target="_top">AWS API Documentation
*/
default CompletableFuture paginatedOperationWithoutResultKey(
- Consumer paginatedOperationWithoutResultKeyRequest) {
+ Consumer paginatedOperationWithoutResultKeyRequest) {
return paginatedOperationWithoutResultKey(PaginatedOperationWithoutResultKeyRequest.builder()
- .apply(paginatedOperationWithoutResultKeyRequest).build());
+ .apply(paginatedOperationWithoutResultKeyRequest).build());
+ }
+
+ /**
+ * Some paginated operation without result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithoutResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation. The return type is a custom publisher that can be subscribed to request a stream of response pages.
+ * SDK will internally handle making service calls for you.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet
+ * and so there is no guarantee that the request is valid. If there are errors in your request, you will see the
+ * failures only after you start streaming the data. The subscribe method should be called as a request to start
+ * streaming data. For more info, see
+ * {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the subscribe
+ * method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data from the
+ * starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithoutResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @param paginatedOperationWithoutResultKeyRequest
+ * @return A custom publisher that can be subscribed to request a stream of response pages.
+ * The CompletableFuture returned by this method can be completed exceptionally with the following
+ * exceptions.
+ *
+ * - SdkException Base class for all exceptions that can be thrown by the SDK (both service and client).
+ * Can be used for catch all scenarios.
+ * - SdkClientException If any client side error occurs such as an IO related failure, failure to get
+ * credentials, etc.
+ * - JsonException Base class for all service exceptions. Unknown exceptions will be thrown as an instance
+ * of this type.
+ *
+ * @sample JsonAsyncClient.PaginatedOperationWithoutResultKey
+ * @see AWS API Documentation
+ */
+ default PaginatedOperationWithoutResultKeyPublisher paginatedOperationWithoutResultKeyPaginator(
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) {
+ throw new UnsupportedOperationException();
}
/**
@@ -388,7 +630,7 @@ default CompletableFuture paginatedO
* target="_top">AWS API Documentation
*/
default CompletableFuture streamingInputOperation(
- StreamingInputOperationRequest streamingInputOperationRequest, AsyncRequestProvider requestProvider) {
+ StreamingInputOperationRequest streamingInputOperationRequest, AsyncRequestProvider requestProvider) {
throw new UnsupportedOperationException();
}
@@ -417,7 +659,7 @@ default CompletableFuture streamingInputOperati
* target="_top">AWS API Documentation
*/
default CompletableFuture streamingInputOperation(
- StreamingInputOperationRequest streamingInputOperationRequest, Path path) {
+ StreamingInputOperationRequest streamingInputOperationRequest, Path path) {
return streamingInputOperation(streamingInputOperationRequest, AsyncRequestProvider.fromFile(path));
}
@@ -446,8 +688,8 @@ default CompletableFuture streamingInputOperati
* target="_top">AWS API Documentation
*/
default CompletableFuture streamingOutputOperation(
- StreamingOutputOperationRequest streamingOutputOperationRequest,
- AsyncResponseHandler asyncResponseHandler) {
+ StreamingOutputOperationRequest streamingOutputOperationRequest,
+ AsyncResponseHandler asyncResponseHandler) {
throw new UnsupportedOperationException();
}
@@ -475,7 +717,7 @@ default CompletableFuture streamingOutputOperation(
* target="_top">AWS API Documentation
*/
default CompletableFuture streamingOutputOperation(
- StreamingOutputOperationRequest streamingOutputOperationRequest, Path path) {
+ StreamingOutputOperationRequest streamingOutputOperationRequest, Path path) {
return streamingOutputOperation(streamingOutputOperationRequest, AsyncResponseHandler.toFile(path));
}
}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java
index 84c92e11fbb5..149f0edd4cfb 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java
@@ -34,8 +34,8 @@
import software.amazon.awssdk.services.json.model.StreamingInputOperationResponse;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationRequest;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationResponse;
-import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator;
-import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable;
import software.amazon.awssdk.services.json.transform.APostOperationRequestMarshaller;
import software.amazon.awssdk.services.json.transform.APostOperationResponseUnmarshaller;
import software.amazon.awssdk.services.json.transform.APostOperationWithOutputRequestMarshaller;
@@ -101,14 +101,14 @@ public APostOperationResponse aPostOperation(APostOperationRequest aPostOperatio
SdkServiceException, SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new APostOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new APostOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler.execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(aPostOperationRequest).withMarshaller(new APostOperationRequestMarshaller(protocolFactory)));
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(aPostOperationRequest).withMarshaller(new APostOperationRequestMarshaller(protocolFactory)));
}
/**
@@ -133,20 +133,20 @@ public APostOperationResponse aPostOperation(APostOperationRequest aPostOperatio
*/
@Override
public APostOperationWithOutputResponse aPostOperationWithOutput(
- APostOperationWithOutputRequest aPostOperationWithOutputRequest) throws InvalidInputException, SdkServiceException,
- SdkClientException, JsonException {
+ APostOperationWithOutputRequest aPostOperationWithOutputRequest) throws InvalidInputException, SdkServiceException,
+ SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new APostOperationWithOutputResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new APostOperationWithOutputResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(aPostOperationWithOutputRequest)
- .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory)));
+ .execute(new ClientExecutionParams()
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(aPostOperationWithOutputRequest)
+ .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory)));
}
/**
@@ -171,20 +171,20 @@ public APostOperationWithOutputResponse aPostOperationWithOutput(
*/
@Override
public GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
- GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) throws InvalidInputException, SdkServiceException,
- SdkClientException, JsonException {
+ GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) throws InvalidInputException, SdkServiceException,
+ SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new GetWithoutRequiredMembersResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new GetWithoutRequiredMembersResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(getWithoutRequiredMembersRequest)
- .withMarshaller(new GetWithoutRequiredMembersRequestMarshaller(protocolFactory)));
+ .execute(new ClientExecutionParams()
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(getWithoutRequiredMembersRequest)
+ .withMarshaller(new GetWithoutRequiredMembersRequestMarshaller(protocolFactory)));
}
/**
@@ -205,20 +205,20 @@ public GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
*/
@Override
public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new PaginatedOperationWithResultKeyResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new PaginatedOperationWithResultKeyResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(paginatedOperationWithResultKeyRequest)
- .withMarshaller(new PaginatedOperationWithResultKeyRequestMarshaller(protocolFactory)));
+ .execute(new ClientExecutionParams()
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(paginatedOperationWithResultKeyRequest)
+ .withMarshaller(new PaginatedOperationWithResultKeyRequestMarshaller(protocolFactory)));
}
/**
@@ -243,7 +243,7 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -253,8 +253,8 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
* {
* @code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client
- * .paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client
+ * .paginatedOperationWithResultKeyPaginator(request);
* for (software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response : responses) {
* // do something;
* }
@@ -265,7 +265,7 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -276,7 +276,7 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* @param paginatedOperationWithResultKeyRequest
- * @return Result of the PaginatedOperationWithResultKey operation returned by the service.
+ * @return A custom iterable that can be used to iterate through all the response pages.
* @throws SdkException
* Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
* catch all scenarios.
@@ -289,10 +289,10 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
* target="_top">AWS API Documentation
*/
@Override
- public PaginatedOperationWithResultKeyPaginator paginatedOperationWithResultKeyIterable(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
- return new PaginatedOperationWithResultKeyPaginator(this, paginatedOperationWithResultKeyRequest);
+ public PaginatedOperationWithResultKeyIterable paginatedOperationWithResultKeyPaginator(
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
+ return new PaginatedOperationWithResultKeyIterable(this, paginatedOperationWithResultKeyRequest);
}
/**
@@ -313,20 +313,20 @@ public PaginatedOperationWithResultKeyPaginator paginatedOperationWithResultKeyI
*/
@Override
public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResultKey(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new PaginatedOperationWithoutResultKeyResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new PaginatedOperationWithoutResultKeyResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(paginatedOperationWithoutResultKeyRequest)
- .withMarshaller(new PaginatedOperationWithoutResultKeyRequestMarshaller(protocolFactory)));
+ .execute(new ClientExecutionParams()
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(paginatedOperationWithoutResultKeyRequest)
+ .withMarshaller(new PaginatedOperationWithoutResultKeyRequestMarshaller(protocolFactory)));
}
/**
@@ -351,7 +351,7 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -361,8 +361,8 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul
*
* {
* @code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client
- * .paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client
+ * .paginatedOperationWithoutResultKeyPaginator(request);
* for (software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyResponse response : responses) {
* // do something;
* }
@@ -373,7 +373,7 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -384,7 +384,7 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul
*
*
* @param paginatedOperationWithoutResultKeyRequest
- * @return Result of the PaginatedOperationWithoutResultKey operation returned by the service.
+ * @return A custom iterable that can be used to iterate through all the response pages.
* @throws SdkException
* Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
* catch all scenarios.
@@ -397,10 +397,10 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul
* target="_top">AWS API Documentation
*/
@Override
- public PaginatedOperationWithoutResultKeyPaginator paginatedOperationWithoutResultKeyIterable(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
- return new PaginatedOperationWithoutResultKeyPaginator(this, paginatedOperationWithoutResultKeyRequest);
+ public PaginatedOperationWithoutResultKeyIterable paginatedOperationWithoutResultKeyPaginator(
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
+ return new PaginatedOperationWithoutResultKeyIterable(this, paginatedOperationWithoutResultKeyRequest);
}
/**
@@ -435,18 +435,18 @@ public StreamingInputOperationResponse streamingInputOperation(StreamingInputOpe
RequestBody requestBody) throws SdkServiceException, SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
- new StreamingInputOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(true).withHasStreamingSuccessResponse(false),
+ new StreamingInputOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler.execute(new ClientExecutionParams()
- .withResponseHandler(responseHandler)
- .withErrorResponseHandler(errorResponseHandler)
- .withInput(streamingInputOperationRequest)
- .withMarshaller(
- new StreamingRequestMarshaller(
- new StreamingInputOperationRequestMarshaller(protocolFactory), requestBody)));
+ .withResponseHandler(responseHandler)
+ .withErrorResponseHandler(errorResponseHandler)
+ .withInput(streamingInputOperationRequest)
+ .withMarshaller(
+ new StreamingRequestMarshaller(
+ new StreamingInputOperationRequestMarshaller(protocolFactory), requestBody)));
}
/**
@@ -475,21 +475,21 @@ public StreamingInputOperationResponse streamingInputOperation(StreamingInputOpe
@Override
public ReturnT streamingOutputOperation(StreamingOutputOperationRequest streamingOutputOperationRequest,
StreamingResponseHandler streamingResponseHandler)
- throws SdkServiceException, SdkClientException, JsonException {
+ throws SdkServiceException, SdkClientException, JsonException {
HttpResponseHandler responseHandler = protocolFactory.createResponseHandler(
- new JsonOperationMetadata().withPayloadJson(false).withHasStreamingSuccessResponse(true),
- new StreamingOutputOperationResponseUnmarshaller());
+ new JsonOperationMetadata().withPayloadJson(false).withHasStreamingSuccessResponse(true),
+ new StreamingOutputOperationResponseUnmarshaller());
HttpResponseHandler errorResponseHandler = createErrorResponseHandler();
return clientHandler
- .execute(
- new ClientExecutionParams()
- .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
- .withInput(streamingOutputOperationRequest)
- .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory)),
- streamingResponseHandler);
+ .execute(
+ new ClientExecutionParams()
+ .withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
+ .withInput(streamingOutputOperationRequest)
+ .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory)),
+ streamingResponseHandler);
}
private HttpResponseHandler createErrorResponseHandler() {
@@ -498,13 +498,13 @@ private HttpResponseHandler createErrorResponseHandler() {
private software.amazon.awssdk.core.protocol.json.SdkJsonProtocolFactory init() {
return new SdkJsonProtocolFactory(new JsonClientMetadata()
- .withProtocolVersion("1.1")
- .withSupportsCbor(false)
- .withSupportsIon(false)
- .withBaseServiceExceptionClass(software.amazon.awssdk.services.json.model.JsonException.class)
- .withContentTypeOverride("")
- .addErrorMetadata(
- new JsonErrorShapeMetadata().withErrorCode("InvalidInput").withModeledClass(InvalidInputException.class)));
+ .withProtocolVersion("1.1")
+ .withSupportsCbor(false)
+ .withSupportsIon(false)
+ .withBaseServiceExceptionClass(software.amazon.awssdk.services.json.model.JsonException.class)
+ .withContentTypeOverride("")
+ .addErrorMetadata(
+ new JsonErrorShapeMetadata().withErrorCode("InvalidInput").withModeledClass(InvalidInputException.class)));
}
@Override
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-interface.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-interface.java
index 5a3018822658..fce4db7ac788 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-interface.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-interface.java
@@ -27,8 +27,8 @@
import software.amazon.awssdk.services.json.model.StreamingInputOperationResponse;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationRequest;
import software.amazon.awssdk.services.json.model.StreamingOutputOperationResponse;
-import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator;
-import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable;
+import software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable;
import software.amazon.awssdk.utils.SdkAutoCloseable;
/**
@@ -102,7 +102,7 @@ default APostOperationResponse aPostOperation(APostOperationRequest aPostOperati
* API Documentation
*/
default APostOperationResponse aPostOperation(Consumer aPostOperationRequest)
- throws InvalidInputException, SdkServiceException, SdkClientException, JsonException {
+ throws InvalidInputException, SdkServiceException, SdkClientException, JsonException {
return aPostOperation(APostOperationRequest.builder().apply(aPostOperationRequest).build());
}
@@ -127,8 +127,8 @@ default APostOperationResponse aPostOperation(ConsumerAWS API Documentation
*/
default APostOperationWithOutputResponse aPostOperationWithOutput(
- APostOperationWithOutputRequest aPostOperationWithOutputRequest) throws InvalidInputException, SdkServiceException,
- SdkClientException, JsonException {
+ APostOperationWithOutputRequest aPostOperationWithOutputRequest) throws InvalidInputException, SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -153,8 +153,8 @@ default APostOperationWithOutputResponse aPostOperationWithOutput(
* target="_top">AWS API Documentation
*/
default APostOperationWithOutputResponse aPostOperationWithOutput(
- Consumer aPostOperationWithOutputRequest) throws InvalidInputException,
- SdkServiceException, SdkClientException, JsonException {
+ Consumer aPostOperationWithOutputRequest) throws InvalidInputException,
+ SdkServiceException, SdkClientException, JsonException {
return aPostOperationWithOutput(APostOperationWithOutputRequest.builder().apply(aPostOperationWithOutputRequest).build());
}
@@ -204,8 +204,8 @@ default GetWithoutRequiredMembersResponse getWithoutRequiredMembers() throws Inv
* target="_top">AWS API Documentation
*/
default GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
- GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) throws InvalidInputException, SdkServiceException,
- SdkClientException, JsonException {
+ GetWithoutRequiredMembersRequest getWithoutRequiredMembersRequest) throws InvalidInputException, SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -230,12 +230,33 @@ default GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
* target="_top">AWS API Documentation
*/
default GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
- Consumer getWithoutRequiredMembersRequest) throws InvalidInputException,
- SdkServiceException, SdkClientException, JsonException {
+ Consumer getWithoutRequiredMembersRequest) throws InvalidInputException,
+ SdkServiceException, SdkClientException, JsonException {
return getWithoutRequiredMembers(GetWithoutRequiredMembersRequest.builder().apply(getWithoutRequiredMembersRequest)
.build());
}
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * @return Result of the PaginatedOperationWithResultKey operation returned by the service.
+ * @throws SdkException
+ * Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
+ * catch all scenarios.
+ * @throws SdkClientException
+ * If any client side error occurs such as an IO related failure, failure to get credentials, etc.
+ * @throws JsonException
+ * Base class for all service exceptions. Unknown exceptions will be thrown as an instance of this type.
+ * @sample JsonClient.PaginatedOperationWithResultKey
+ * @see #paginatedOperationWithResultKey(PaginatedOperationWithResultKeyRequest)
+ * @see AWS API Documentation
+ */
+ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey() throws SdkServiceException,
+ SdkClientException, JsonException {
+ return paginatedOperationWithResultKey(PaginatedOperationWithResultKeyRequest.builder().build());
+ }
+
/**
* Some paginated operation with result_key in paginators.json file
*
@@ -253,8 +274,8 @@ default GetWithoutRequiredMembersResponse getWithoutRequiredMembers(
* target="_top">AWS API Documentation
*/
default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -275,8 +296,8 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
* target="_top">AWS API Documentation
*/
default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
- Consumer paginatedOperationWithResultKeyRequest)
- throws SdkServiceException, SdkClientException, JsonException {
+ Consumer paginatedOperationWithResultKeyRequest)
+ throws SdkServiceException, SdkClientException, JsonException {
return paginatedOperationWithResultKey(PaginatedOperationWithResultKeyRequest.builder()
.apply(paginatedOperationWithResultKeyRequest).build());
}
@@ -303,7 +324,7 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -313,8 +334,8 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
* {
* @code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client
- * .paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client
+ * .paginatedOperationWithResultKeyPaginator(request);
* for (software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response : responses) {
* // do something;
* }
@@ -325,7 +346,79 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
+ * responses.iterator().forEachRemaining(....);
+ * }
+ *
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation.
+ *
+ *
+ * @return A custom iterable that can be used to iterate through all the response pages.
+ * @throws SdkException
+ * Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
+ * catch all scenarios.
+ * @throws SdkClientException
+ * If any client side error occurs such as an IO related failure, failure to get credentials, etc.
+ * @throws JsonException
+ * Base class for all service exceptions. Unknown exceptions will be thrown as an instance of this type.
+ * @sample JsonClient.PaginatedOperationWithResultKey
+ * @see #paginatedOperationWithResultKeyPaginator(PaginatedOperationWithResultKeyRequest)
+ * @see AWS API Documentation
+ */
+ default PaginatedOperationWithResultKeyIterable paginatedOperationWithResultKeyPaginator() throws SdkServiceException,
+ SdkClientException, JsonException {
+ return paginatedOperationWithResultKeyPaginator(PaginatedOperationWithResultKeyRequest.builder().build());
+ }
+
+ /**
+ * Some paginated operation with result_key in paginators.json file
+ *
+ * This is a variant of
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyRequest)}
+ * operation. The return type is a custom iterable that can be used to iterate through all the pages. SDK will
+ * internally handle making service calls for you.
+ *
+ *
+ * When this operation is called, a custom iterable is returned but no service calls are made yet. So there is no
+ * guarantee that the request is valid. As you iterate through the iterable, SDK will start lazily loading response
+ * pages by making service calls until there are no pages left or your iteration stops. If there are errors in your
+ * request, you will see the failures only after you start iterating through the iterable.
+ *
+ *
+ *
+ * The following are few ways to iterate through the response pages:
+ *
+ * 1) Using a Stream
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
+ * responses.stream().forEach(....);
+ * }
+ *
+ *
+ * 2) Using For loop
+ *
+ *
+ * {
+ * @code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client
+ * .paginatedOperationWithResultKeyPaginator(request);
+ * for (software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response : responses) {
+ * // do something;
+ * }
+ * }
+ *
+ *
+ * 3) Use iterator directly
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -336,7 +429,7 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
*
*
* @param paginatedOperationWithResultKeyRequest
- * @return Result of the PaginatedOperationWithResultKey operation returned by the service.
+ * @return A custom iterable that can be used to iterate through all the response pages.
* @throws SdkException
* Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
* catch all scenarios.
@@ -348,9 +441,9 @@ default PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey(
* @see AWS API Documentation
*/
- default PaginatedOperationWithResultKeyPaginator paginatedOperationWithResultKeyIterable(
- PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ default PaginatedOperationWithResultKeyIterable paginatedOperationWithResultKeyPaginator(
+ PaginatedOperationWithResultKeyRequest paginatedOperationWithResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -371,8 +464,8 @@ default PaginatedOperationWithResultKeyPaginator paginatedOperationWithResultKey
* target="_top">AWS API Documentation
*/
default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResultKey(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -393,8 +486,8 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
* target="_top">AWS API Documentation
*/
default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResultKey(
- Consumer paginatedOperationWithoutResultKeyRequest)
- throws SdkServiceException, SdkClientException, JsonException {
+ Consumer paginatedOperationWithoutResultKeyRequest)
+ throws SdkServiceException, SdkClientException, JsonException {
return paginatedOperationWithoutResultKey(PaginatedOperationWithoutResultKeyRequest.builder()
.apply(paginatedOperationWithoutResultKeyRequest).build());
}
@@ -421,7 +514,7 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -431,8 +524,8 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
*
* {
* @code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client
- * .paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client
+ * .paginatedOperationWithoutResultKeyPaginator(request);
* for (software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyResponse response : responses) {
* // do something;
* }
@@ -443,7 +536,7 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
*
*
* {@code
- * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -454,7 +547,7 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
*
*
* @param paginatedOperationWithoutResultKeyRequest
- * @return Result of the PaginatedOperationWithoutResultKey operation returned by the service.
+ * @return A custom iterable that can be used to iterate through all the response pages.
* @throws SdkException
* Base class for all exceptions that can be thrown by the SDK (both service and client). Can be used for
* catch all scenarios.
@@ -466,9 +559,9 @@ default PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResu
* @see AWS API Documentation
*/
- default PaginatedOperationWithoutResultKeyPaginator paginatedOperationWithoutResultKeyIterable(
- PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
- SdkClientException, JsonException {
+ default PaginatedOperationWithoutResultKeyIterable paginatedOperationWithoutResultKeyPaginator(
+ PaginatedOperationWithoutResultKeyRequest paginatedOperationWithoutResultKeyRequest) throws SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -500,8 +593,8 @@ default PaginatedOperationWithoutResultKeyPaginator paginatedOperationWithoutRes
* target="_top">AWS API Documentation
*/
default StreamingInputOperationResponse streamingInputOperation(
- StreamingInputOperationRequest streamingInputOperationRequest, RequestBody requestBody) throws SdkServiceException,
- SdkClientException, JsonException {
+ StreamingInputOperationRequest streamingInputOperationRequest, RequestBody requestBody) throws SdkServiceException,
+ SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -528,8 +621,8 @@ default StreamingInputOperationResponse streamingInputOperation(
* target="_top">AWS API Documentation
*/
default StreamingInputOperationResponse streamingInputOperation(
- StreamingInputOperationRequest streamingInputOperationRequest, Path filePath) throws SdkServiceException,
- SdkClientException, JsonException {
+ StreamingInputOperationRequest streamingInputOperationRequest, Path filePath) throws SdkServiceException,
+ SdkClientException, JsonException {
return streamingInputOperation(streamingInputOperationRequest, RequestBody.of(filePath));
}
@@ -558,7 +651,7 @@ default StreamingInputOperationResponse streamingInputOperation(
*/
default ReturnT streamingOutputOperation(StreamingOutputOperationRequest streamingOutputOperationRequest,
StreamingResponseHandler streamingResponseHandler)
- throws SdkServiceException, SdkClientException, JsonException {
+ throws SdkServiceException, SdkClientException, JsonException {
throw new UnsupportedOperationException();
}
@@ -584,8 +677,8 @@ default ReturnT streamingOutputOperation(StreamingOutputOperationReque
* target="_top">AWS API Documentation
*/
default StreamingOutputOperationResponse streamingOutputOperation(
- StreamingOutputOperationRequest streamingOutputOperationRequest, Path filePath) throws SdkServiceException,
- SdkClientException, JsonException {
+ StreamingOutputOperationRequest streamingOutputOperationRequest, Path filePath) throws SdkServiceException,
+ SdkClientException, JsonException {
return streamingOutputOperation(streamingOutputOperationRequest, StreamingResponseHandler.toFile(filePath));
}
@@ -612,8 +705,8 @@ default StreamingOutputOperationResponse streamingOutputOperation(
* target="_top">AWS API Documentation
*/
default ResponseInputStream streamingOutputOperation(
- StreamingOutputOperationRequest streamingOutputOperationRequest) throws SdkServiceException, SdkClientException,
- JsonException {
+ StreamingOutputOperationRequest streamingOutputOperationRequest) throws SdkServiceException, SdkClientException,
+ JsonException {
return streamingOutputOperation(streamingOutputOperationRequest, StreamingResponseHandler.toInputStream());
}
@@ -638,8 +731,8 @@ default ResponseInputStream streamingOutputOpe
* target="_top">AWS API Documentation
*/
default ResponseBytes streamingOutputOperationBytes(
- StreamingOutputOperationRequest streamingOutputOperationRequest) throws SdkServiceException, SdkClientException,
- JsonException {
+ StreamingOutputOperationRequest streamingOutputOperationRequest) throws SdkServiceException, SdkClientException,
+ JsonException {
return streamingOutputOperation(streamingOutputOperationRequest, StreamingResponseHandler.toBytes());
}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPaginator.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyIterable.java
similarity index 65%
rename from codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPaginator.java
rename to codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyIterable.java
index 88343f47d010..4092395e373a 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPaginator.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyIterable.java
@@ -1,12 +1,13 @@
package software.amazon.awssdk.services.jsonprotocoltests.paginators;
+import java.util.Collections;
import java.util.Iterator;
import java.util.function.Function;
import javax.annotation.Generated;
-import software.amazon.awssdk.core.pagination.NextPageFetcher;
import software.amazon.awssdk.core.pagination.PaginatedItemsIterable;
import software.amazon.awssdk.core.pagination.PaginatedResponsesIterator;
import software.amazon.awssdk.core.pagination.SdkIterable;
+import software.amazon.awssdk.core.pagination.SyncPageFetcher;
import software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient;
import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest;
import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse;
@@ -15,7 +16,7 @@
/**
*
* Represents the output for the
- * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient#paginatedOperationWithResultKeyIterable(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest)}
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient#paginatedOperationWithResultKeyPaginator(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest)}
* operation which is a paginated operation. This class is an iterable of
* {@link software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse} that can be
* used to iterate through all the response pages of the operation.
@@ -34,7 +35,7 @@
*
*
* {@code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -44,8 +45,8 @@
*
* {
* @code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyPaginator responses = client
- * .paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyIterable responses = client
+ * .paginatedOperationWithResultKeyPaginator(request);
* for (software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse response : responses) {
* // do something;
* }
@@ -56,7 +57,7 @@
*
*
* {@code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyPaginator responses = client.paginatedOperationWithResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyIterable responses = client.paginatedOperationWithResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -67,15 +68,15 @@
*
*/
@Generated("software.amazon.awssdk:codegen")
-public final class PaginatedOperationWithResultKeyPaginator implements SdkIterable {
+public class PaginatedOperationWithResultKeyIterable implements SdkIterable {
private final JsonProtocolTestsClient client;
private final PaginatedOperationWithResultKeyRequest firstRequest;
- private final NextPageFetcher nextPageFetcher;
+ private final SyncPageFetcher nextPageFetcher;
- public PaginatedOperationWithResultKeyPaginator(final JsonProtocolTestsClient client,
- final PaginatedOperationWithResultKeyRequest firstRequest) {
+ public PaginatedOperationWithResultKeyIterable(final JsonProtocolTestsClient client,
+ final PaginatedOperationWithResultKeyRequest firstRequest) {
this.client = client;
this.firstRequest = firstRequest;
this.nextPageFetcher = new PaginatedOperationWithResultKeyResponseFetcher();
@@ -95,15 +96,38 @@ public Iterator iterator() {
* of the top level pages. Similar to iteration over pages, this method internally makes service calls to get the
* next list of results until the iteration stops or there are no more results.
*/
- public SdkIterable items() {
- Function> getIterator = response -> response != null ? response
- .items().iterator() : null;
-
+ public final SdkIterable items() {
+ Function> getIterator = response -> {
+ if (response != null && response.items() != null) {
+ return response.items().iterator();
+ }
+ return Collections.emptyIterator();
+ };
return new PaginatedItemsIterable(this, getIterator);
}
+ /**
+ *
+ * A helper method to resume the pages in case of unexpected failures. The method takes the last successful response
+ * page as input and returns an instance of {@link PaginatedOperationWithResultKeyIterable} that can be used to
+ * retrieve the consecutive pages that follows the input page.
+ *
+ */
+ public final PaginatedOperationWithResultKeyIterable resume(final PaginatedOperationWithResultKeyResponse lastSuccessfulPage) {
+ if (nextPageFetcher.hasNextPage(lastSuccessfulPage)) {
+ return new PaginatedOperationWithResultKeyIterable(client, firstRequest.toBuilder()
+ .nextToken(lastSuccessfulPage.nextToken()).build());
+ }
+ return new PaginatedOperationWithResultKeyIterable(client, firstRequest) {
+ @Override
+ public Iterator iterator() {
+ return Collections.emptyIterator();
+ }
+ };
+ }
+
private class PaginatedOperationWithResultKeyResponseFetcher implements
- NextPageFetcher {
+ SyncPageFetcher {
@Override
public boolean hasNextPage(PaginatedOperationWithResultKeyResponse previousPage) {
return previousPage.nextToken() != null;
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPublisher.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPublisher.java
new file mode 100644
index 000000000000..6b1255321fd9
--- /dev/null
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithResultKeyPublisher.java
@@ -0,0 +1,150 @@
+package software.amazon.awssdk.services.jsonprotocoltests.paginators;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+import javax.annotation.Generated;
+import org.reactivestreams.Subscriber;
+import software.amazon.awssdk.core.pagination.async.AsyncPageFetcher;
+import software.amazon.awssdk.core.pagination.async.EmptySubscription;
+import software.amazon.awssdk.core.pagination.async.PaginatedItemsPublisher;
+import software.amazon.awssdk.core.pagination.async.ResponsesSubscription;
+import software.amazon.awssdk.core.pagination.async.SdkPublisher;
+import software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsAsyncClient;
+import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest;
+import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse;
+import software.amazon.awssdk.services.jsonprotocoltests.model.SimpleStruct;
+
+/**
+ *
+ * Represents the output for the
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsAsyncClient#paginatedOperationWithResultKeyPaginator(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest)}
+ * operation which is a paginated operation. This class is a type of {@link org.reactivestreams.Publisher} which can be
+ * used to provide a sequence of
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse} response
+ * pages as per demand from the subscriber.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet and
+ * so there is no guarantee that the request is valid. If there are errors in your request, you will see the failures
+ * only after you start streaming the data. The subscribe method should be called as a request to start streaming data.
+ * For more info, see {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the
+ * subscribe method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data
+ * from the starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithResultKey(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithResultKeyRequest)}
+ * operation.
+ *
+ */
+@Generated("software.amazon.awssdk:codegen")
+public class PaginatedOperationWithResultKeyPublisher implements SdkPublisher {
+ private final JsonProtocolTestsAsyncClient client;
+
+ private final PaginatedOperationWithResultKeyRequest firstRequest;
+
+ private final AsyncPageFetcher nextPageFetcher;
+
+ private boolean isLastPage;
+
+ public PaginatedOperationWithResultKeyPublisher(final JsonProtocolTestsAsyncClient client,
+ final PaginatedOperationWithResultKeyRequest firstRequest) {
+ this(client, firstRequest, false);
+ }
+
+ private PaginatedOperationWithResultKeyPublisher(final JsonProtocolTestsAsyncClient client,
+ final PaginatedOperationWithResultKeyRequest firstRequest, final boolean isLastPage) {
+ this.client = client;
+ this.firstRequest = firstRequest;
+ this.isLastPage = isLastPage;
+ this.nextPageFetcher = new PaginatedOperationWithResultKeyResponseFetcher();
+ }
+
+ @Override
+ public void subscribe(Subscriber super PaginatedOperationWithResultKeyResponse> subscriber) {
+ subscriber.onSubscribe(new ResponsesSubscription(subscriber, nextPageFetcher));
+ }
+
+ /**
+ * Returns a publisher that can be used to get a stream of data. You need to subscribe to the publisher to request
+ * the stream of data. The publisher has a helper forEach method that takes in a {@link java.util.function.Consumer}
+ * and then applies that consumer to each response returned by the service.
+ */
+ public final SdkPublisher items() {
+ Function> getIterator = response -> {
+ if (response != null && response.items() != null) {
+ return response.items().iterator();
+ }
+ return Collections.emptyIterator();
+ };
+ return new PaginatedItemsPublisher(new PaginatedOperationWithResultKeyResponseFetcher(), getIterator, isLastPage);
+ }
+
+ /**
+ *
+ * A helper method to resume the pages in case of unexpected failures. The method takes the last successful response
+ * page as input and returns an instance of {@link PaginatedOperationWithResultKeyPublisher} that can be used to
+ * retrieve the consecutive pages that follows the input page.
+ *
+ */
+ public final PaginatedOperationWithResultKeyPublisher resume(final PaginatedOperationWithResultKeyResponse lastSuccessfulPage) {
+ if (nextPageFetcher.hasNextPage(lastSuccessfulPage)) {
+ return new PaginatedOperationWithResultKeyPublisher(client, firstRequest.toBuilder()
+ .nextToken(lastSuccessfulPage.nextToken()).build());
+ }
+ return new PaginatedOperationWithResultKeyPublisher(client, firstRequest, true) {
+ @Override
+ public void subscribe(Subscriber super PaginatedOperationWithResultKeyResponse> subscriber) {
+ subscriber.onSubscribe(new EmptySubscription(subscriber));
+ }
+ };
+ }
+
+ private class PaginatedOperationWithResultKeyResponseFetcher implements
+ AsyncPageFetcher {
+ @Override
+ public boolean hasNextPage(final PaginatedOperationWithResultKeyResponse previousPage) {
+ return previousPage.nextToken() != null;
+ }
+
+ @Override
+ public CompletableFuture nextPage(
+ final PaginatedOperationWithResultKeyResponse previousPage) {
+ if (previousPage == null) {
+ return client.paginatedOperationWithResultKey(firstRequest);
+ }
+ return client.paginatedOperationWithResultKey(firstRequest.toBuilder().nextToken(previousPage.nextToken()).build());
+ }
+ }
+}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPaginator.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyIterable.java
similarity index 62%
rename from codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPaginator.java
rename to codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyIterable.java
index 7a77a8114b8d..22293415188d 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPaginator.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyIterable.java
@@ -1,10 +1,11 @@
package software.amazon.awssdk.services.jsonprotocoltests.paginators;
+import java.util.Collections;
import java.util.Iterator;
import javax.annotation.Generated;
-import software.amazon.awssdk.core.pagination.NextPageFetcher;
import software.amazon.awssdk.core.pagination.PaginatedResponsesIterator;
import software.amazon.awssdk.core.pagination.SdkIterable;
+import software.amazon.awssdk.core.pagination.SyncPageFetcher;
import software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient;
import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest;
import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse;
@@ -12,7 +13,7 @@
/**
*
* Represents the output for the
- * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient#paginatedOperationWithoutResultKeyIterable(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest)}
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsClient#paginatedOperationWithoutResultKeyPaginator(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest)}
* operation which is a paginated operation. This class is an iterable of
* {@link software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse} that can
* be used to iterate through all the response pages of the operation.
@@ -31,7 +32,7 @@
*
*
* {@code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.stream().forEach(....);
* }
*
@@ -41,8 +42,8 @@
*
* {
* @code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client
- * .paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyIterable responses = client
+ * .paginatedOperationWithoutResultKeyPaginator(request);
* for (software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse response : responses) {
* // do something;
* }
@@ -53,7 +54,7 @@
*
*
* {@code
- * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyPaginator responses = client.paginatedOperationWithoutResultKeyIterable(request);
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyIterable responses = client.paginatedOperationWithoutResultKeyPaginator(request);
* responses.iterator().forEachRemaining(....);
* }
*
@@ -64,15 +65,15 @@
*
*/
@Generated("software.amazon.awssdk:codegen")
-public final class PaginatedOperationWithoutResultKeyPaginator implements SdkIterable {
+public class PaginatedOperationWithoutResultKeyIterable implements SdkIterable {
private final JsonProtocolTestsClient client;
private final PaginatedOperationWithoutResultKeyRequest firstRequest;
- private final NextPageFetcher nextPageFetcher;
+ private final SyncPageFetcher nextPageFetcher;
- public PaginatedOperationWithoutResultKeyPaginator(final JsonProtocolTestsClient client,
- final PaginatedOperationWithoutResultKeyRequest firstRequest) {
+ public PaginatedOperationWithoutResultKeyIterable(final JsonProtocolTestsClient client,
+ final PaginatedOperationWithoutResultKeyRequest firstRequest) {
this.client = client;
this.firstRequest = firstRequest;
this.nextPageFetcher = new PaginatedOperationWithoutResultKeyResponseFetcher();
@@ -83,8 +84,29 @@ public Iterator iterator() {
return new PaginatedResponsesIterator(nextPageFetcher);
}
+ /**
+ *
+ * A helper method to resume the pages in case of unexpected failures. The method takes the last successful response
+ * page as input and returns an instance of {@link PaginatedOperationWithoutResultKeyIterable} that can be used to
+ * retrieve the consecutive pages that follows the input page.
+ *
+ */
+ public final PaginatedOperationWithoutResultKeyIterable resume(
+ final PaginatedOperationWithoutResultKeyResponse lastSuccessfulPage) {
+ if (nextPageFetcher.hasNextPage(lastSuccessfulPage)) {
+ return new PaginatedOperationWithoutResultKeyIterable(client, firstRequest.toBuilder()
+ .nextToken(lastSuccessfulPage.nextToken()).build());
+ }
+ return new PaginatedOperationWithoutResultKeyIterable(client, firstRequest) {
+ @Override
+ public Iterator iterator() {
+ return Collections.emptyIterator();
+ }
+ };
+ }
+
private class PaginatedOperationWithoutResultKeyResponseFetcher implements
- NextPageFetcher {
+ SyncPageFetcher {
@Override
public boolean hasNextPage(PaginatedOperationWithoutResultKeyResponse previousPage) {
return previousPage.nextToken() != null;
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPublisher.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPublisher.java
new file mode 100644
index 000000000000..d21c39253759
--- /dev/null
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/PaginatedOperationWithoutResultKeyPublisher.java
@@ -0,0 +1,132 @@
+package software.amazon.awssdk.services.jsonprotocoltests.paginators;
+
+import java.util.concurrent.CompletableFuture;
+import javax.annotation.Generated;
+import org.reactivestreams.Subscriber;
+import software.amazon.awssdk.core.pagination.async.AsyncPageFetcher;
+import software.amazon.awssdk.core.pagination.async.EmptySubscription;
+import software.amazon.awssdk.core.pagination.async.ResponsesSubscription;
+import software.amazon.awssdk.core.pagination.async.SdkPublisher;
+import software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsAsyncClient;
+import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest;
+import software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse;
+
+/**
+ *
+ * Represents the output for the
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.JsonProtocolTestsAsyncClient#paginatedOperationWithoutResultKeyPaginator(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation which is a paginated operation. This class is a type of {@link org.reactivestreams.Publisher} which can be
+ * used to provide a sequence of
+ * {@link software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse} response
+ * pages as per demand from the subscriber.
+ *
+ *
+ * When the operation is called, an instance of this class is returned. At this point, no service calls are made yet and
+ * so there is no guarantee that the request is valid. If there are errors in your request, you will see the failures
+ * only after you start streaming the data. The subscribe method should be called as a request to start streaming data.
+ * For more info, see {@link org.reactivestreams.Publisher#subscribe(org.reactivestreams.Subscriber)}. Each call to the
+ * subscribe method will result in a new {@link org.reactivestreams.Subscription} i.e., a new contract to stream data
+ * from the starting request.
+ *
+ *
+ *
+ * The following are few ways to use the response class:
+ *
+ * 1) Using the forEach helper method
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * CompletableFuture future = publisher.forEach(res -> { // Do something with the response });
+ * future.get();
+ * }
+ *
+ *
+ * 2) Using a custom subscriber
+ *
+ *
+ * {@code
+ * software.amazon.awssdk.services.jsonprotocoltests.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
+ * publisher.subscribe(new Subscriber() {
+ *
+ * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
+ *
+ *
+ * public void onNext(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyResponse response) { //... };
+ * });}
+ *
+ *
+ * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2.
+ *
+ * Note: If you prefer to have control on service calls, use the
+ * {@link #paginatedOperationWithoutResultKey(software.amazon.awssdk.services.jsonprotocoltests.model.PaginatedOperationWithoutResultKeyRequest)}
+ * operation.
+ *
+ */
+@Generated("software.amazon.awssdk:codegen")
+public class PaginatedOperationWithoutResultKeyPublisher implements SdkPublisher {
+ private final JsonProtocolTestsAsyncClient client;
+
+ private final PaginatedOperationWithoutResultKeyRequest firstRequest;
+
+ private final AsyncPageFetcher nextPageFetcher;
+
+ private boolean isLastPage;
+
+ public PaginatedOperationWithoutResultKeyPublisher(final JsonProtocolTestsAsyncClient client,
+ final PaginatedOperationWithoutResultKeyRequest firstRequest) {
+ this(client, firstRequest, false);
+ }
+
+ private PaginatedOperationWithoutResultKeyPublisher(final JsonProtocolTestsAsyncClient client,
+ final PaginatedOperationWithoutResultKeyRequest firstRequest, final boolean isLastPage) {
+ this.client = client;
+ this.firstRequest = firstRequest;
+ this.isLastPage = isLastPage;
+ this.nextPageFetcher = new PaginatedOperationWithoutResultKeyResponseFetcher();
+ }
+
+ @Override
+ public void subscribe(Subscriber super PaginatedOperationWithoutResultKeyResponse> subscriber) {
+ subscriber.onSubscribe(new ResponsesSubscription(subscriber, nextPageFetcher));
+ }
+
+ /**
+ *
+ * A helper method to resume the pages in case of unexpected failures. The method takes the last successful response
+ * page as input and returns an instance of {@link PaginatedOperationWithoutResultKeyPublisher} that can be used to
+ * retrieve the consecutive pages that follows the input page.
+ *
+ */
+ public final PaginatedOperationWithoutResultKeyPublisher resume(
+ final PaginatedOperationWithoutResultKeyResponse lastSuccessfulPage) {
+ if (nextPageFetcher.hasNextPage(lastSuccessfulPage)) {
+ return new PaginatedOperationWithoutResultKeyPublisher(client, firstRequest.toBuilder()
+ .nextToken(lastSuccessfulPage.nextToken()).build());
+ }
+ return new PaginatedOperationWithoutResultKeyPublisher(client, firstRequest, true) {
+ @Override
+ public void subscribe(Subscriber super PaginatedOperationWithoutResultKeyResponse> subscriber) {
+ subscriber.onSubscribe(new EmptySubscription(subscriber));
+ }
+ };
+ }
+
+ private class PaginatedOperationWithoutResultKeyResponseFetcher implements
+ AsyncPageFetcher {
+ @Override
+ public boolean hasNextPage(final PaginatedOperationWithoutResultKeyResponse previousPage) {
+ return previousPage.nextToken() != null;
+ }
+
+ @Override
+ public CompletableFuture nextPage(
+ final PaginatedOperationWithoutResultKeyResponse previousPage) {
+ if (previousPage == null) {
+ return client.paginatedOperationWithoutResultKey(firstRequest);
+ }
+ return client
+ .paginatedOperationWithoutResultKey(firstRequest.toBuilder().nextToken(previousPage.nextToken()).build());
+ }
+ }
+}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/customization.config b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/customization.config
index c90352b1dd80..53d1e1898170 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/customization.config
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/customization.config
@@ -3,5 +3,6 @@
"allTypes",
"nestedContainers",
"operationWithNoInputOrOutput"
- ]
+ ],
+ "verifiedSimpleMethods" : ["paginatedOperationWithResultKey"]
}
\ No newline at end of file
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/service-2.json b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/service-2.json
index 8dff2991dccb..49e9ca1caa8b 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/service-2.json
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/paginators/service-2.json
@@ -294,9 +294,6 @@
"Timestamp":{"type":"timestamp"},
"PaginatedOperationWithResultKeyRequest": {
"type": "structure",
- "required": [
- "NextToken"
- ],
"members": {
"NextToken": {
"shape": "String",
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterable.java b/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterable.java
index 1b241bfaa261..98931eb027d3 100644
--- a/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterable.java
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterable.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.core.pagination;
+import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Function;
@@ -51,17 +52,17 @@ private class ItemsIterator implements Iterator {
ItemsIterator(final Iterator pagesIterator) {
this.pagesIterator = pagesIterator;
- this.singlePageItemsIterator = getItemIterator.apply(pagesIterator.next());
+ this.singlePageItemsIterator = pagesIterator.hasNext() ? getItemIterator.apply(pagesIterator.next())
+ : Collections.emptyIterator();
}
@Override
public boolean hasNext() {
- while ((singlePageItemsIterator == null || !singlePageItemsIterator.hasNext())
- && pagesIterator.hasNext()) {
+ while (!hasMoreItems() && pagesIterator.hasNext()) {
singlePageItemsIterator = getItemIterator.apply(pagesIterator.next());
}
- if (singlePageItemsIterator != null && singlePageItemsIterator.hasNext()) {
+ if (hasMoreItems()) {
return true;
}
@@ -76,6 +77,10 @@ public ItemT next() {
return singlePageItemsIterator.next();
}
+
+ private boolean hasMoreItems() {
+ return singlePageItemsIterator.hasNext();
+ }
}
}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedResponsesIterator.java b/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedResponsesIterator.java
index 05cdfaa2fd51..b0f09bc28a9f 100644
--- a/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedResponsesIterator.java
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/PaginatedResponsesIterator.java
@@ -28,13 +28,13 @@
*/
public class PaginatedResponsesIterator implements Iterator {
- private final NextPageFetcher nextPageFetcher;
+ private final SyncPageFetcher nextPageFetcher;
// This is null when the object is created. It gets initialized in next() method
// where SDK make service calls.
private ResponseT oldResponse;
- public PaginatedResponsesIterator(NextPageFetcher nextPageFetcher) {
+ public PaginatedResponsesIterator(SyncPageFetcher nextPageFetcher) {
this.nextPageFetcher = nextPageFetcher;
}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/NextPageFetcher.java b/core/src/main/java/software/amazon/awssdk/core/pagination/SyncPageFetcher.java
similarity index 96%
rename from core/src/main/java/software/amazon/awssdk/core/pagination/NextPageFetcher.java
rename to core/src/main/java/software/amazon/awssdk/core/pagination/SyncPageFetcher.java
index de4db5a6f49a..a628ad4e3a39 100644
--- a/core/src/main/java/software/amazon/awssdk/core/pagination/NextPageFetcher.java
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/SyncPageFetcher.java
@@ -18,7 +18,7 @@
import software.amazon.awssdk.annotations.SdkInternalApi;
@SdkInternalApi
-public interface NextPageFetcher {
+public interface SyncPageFetcher {
/**
* Returns a boolean value indicating if a next page is available.
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/AsyncPageFetcher.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/AsyncPageFetcher.java
new file mode 100644
index 000000000000..024bb25588ba
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/AsyncPageFetcher.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.concurrent.CompletableFuture;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+
+/**
+ * Interface to deal with async paginated responses.
+ * @param Type of Response
+ */
+@SdkInternalApi
+public interface AsyncPageFetcher {
+
+ /**
+ * Returns a boolean value indicating if a next page is available.
+ *
+ * @param oldPage last page sent by service in a paginated operation
+ * @return True if there is a next page available. Otherwise false.
+ */
+ boolean hasNextPage(ResponseT oldPage);
+
+ /**
+ * Method that uses the information in #oldPage and returns a
+ * completable future for the next page. This method makes service calls.
+ *
+ * @param oldPage last page sent by service in a paginated operation
+ * @return A CompletableFuture that can be used to get the next response page
+ */
+ CompletableFuture nextPage(ResponseT oldPage);
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/EmptySubscription.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/EmptySubscription.java
new file mode 100644
index 000000000000..321788750ada
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/EmptySubscription.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+/**
+ * A NoOp implementation of {@link Subscription} interface.
+ *
+ * This subscription calls {@link Subscriber#onComplete()} on first request for data and then terminates the subscription.
+ */
+public class EmptySubscription implements Subscription {
+
+ private final AtomicBoolean isTerminated = new AtomicBoolean(false);
+ private final Subscriber subscriber;
+
+ public EmptySubscription(Subscriber subscriber) {
+ this.subscriber = subscriber;
+ }
+
+ @Override
+ public void request(long n) {
+ if (isTerminated()) {
+ return;
+ }
+
+ if (n <= 0) {
+ throw new IllegalArgumentException("Non-positive request signals are illegal");
+ }
+
+ if (terminate()) {
+ subscriber.onComplete();
+ }
+ }
+
+ @Override
+ public void cancel() {
+ terminate();
+ }
+
+ private boolean terminate() {
+ return isTerminated.compareAndSet(false, true);
+ }
+
+ private boolean isTerminated() {
+ return isTerminated.get();
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/ItemsSubscription.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/ItemsSubscription.java
new file mode 100644
index 000000000000..4c6a6ba35f6d
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/ItemsSubscription.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.Iterator;
+import java.util.function.Function;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+/**
+ * An implementation of the {@link Subscription} interface that can be used to signal and cancel demand for
+ * paginated items across pages
+ *
+ * @param The type of a single response page
+ * @param The type of paginated member in a response page
+ */
+public class ItemsSubscription extends PaginationSubscription {
+ private final Function> getIteratorFunction;
+ private volatile Iterator singlePageItemsIterator;
+
+ public ItemsSubscription(Subscriber subscriber,
+ AsyncPageFetcher nextPageFetcher,
+ Function> getIteratorFunction) {
+ super(subscriber, nextPageFetcher);
+ this.getIteratorFunction = getIteratorFunction;
+ }
+
+
+ protected void handleRequests() {
+ if (!hasMoreItems() && !hasNextPage()) {
+ completeSubscription();
+ return;
+ }
+
+ synchronized (this) {
+ if (outstandingRequests.get() <= 0) {
+ stopTask();
+ return;
+ }
+ }
+
+ if (!isTerminated()) {
+ /**
+ * Current page is null only the first time the method is called.
+ * Once initialized, current page will never be null
+ */
+ if (currentPage == null || (!hasMoreItems() && hasNextPage())) {
+ fetchNextPage();
+
+ } else if (hasMoreItems()) {
+ sendNextElement();
+
+ // All valid cases are covered above. Throw an exception if any combination is missed
+ } else {
+ throw new IllegalStateException("Execution should have not reached here");
+ }
+ }
+ }
+
+ private void fetchNextPage() {
+ nextPageFetcher.nextPage(currentPage)
+ .whenComplete(((response, error) -> {
+ if (response != null) {
+ currentPage = response;
+ singlePageItemsIterator = getIteratorFunction.apply(response);
+ sendNextElement();
+ }
+ if (error != null) {
+ subscriber.onError(error);
+ cleanup();
+ }
+ }));
+ }
+
+ /**
+ * Calls onNext and calls the recursive method.
+ */
+ private void sendNextElement() {
+ if (singlePageItemsIterator.hasNext()) {
+ subscriber.onNext(singlePageItemsIterator.next());
+ outstandingRequests.getAndDecrement();
+ }
+
+ handleRequests();
+ }
+
+ private boolean hasMoreItems() {
+ return singlePageItemsIterator != null && singlePageItemsIterator.hasNext();
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginatedItemsPublisher.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginatedItemsPublisher.java
new file mode 100644
index 000000000000..c69d251882f1
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginatedItemsPublisher.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.Iterator;
+import java.util.function.Function;
+import org.reactivestreams.Subscriber;
+
+/**
+ * A publisher to request for a stream of paginated items. The class can be used to request data for paginated items
+ * across multiple pages.
+ *
+ * @param The type of a single response page
+ * @param The type of paginated member in a response page
+ */
+public class PaginatedItemsPublisher implements SdkPublisher {
+
+ private final AsyncPageFetcher nextPageFetcher;
+
+ private final Function> getIteratorFunction;
+
+ private final boolean isLastPage;
+
+ public PaginatedItemsPublisher(AsyncPageFetcher nextPageFetcher,
+ Function> getIteratorFunction,
+ boolean isLastPage) {
+ this.nextPageFetcher = nextPageFetcher;
+ this.getIteratorFunction = getIteratorFunction;
+ this.isLastPage = isLastPage;
+ }
+
+ @Override
+ public void subscribe(Subscriber super ItemT> subscriber) {
+ subscriber.onSubscribe(isLastPage ? new EmptySubscription(subscriber)
+ : new ItemsSubscription(subscriber, nextPageFetcher, getIteratorFunction));
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginationSubscription.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginationSubscription.java
new file mode 100644
index 000000000000..e482e5eee0e0
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/PaginationSubscription.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+public abstract class PaginationSubscription implements Subscription {
+
+ protected AtomicLong outstandingRequests = new AtomicLong(0);
+ protected final Subscriber subscriber;
+ protected final AsyncPageFetcher nextPageFetcher;
+ protected volatile ResponseT currentPage;
+
+ // boolean indicating whether subscription is terminated
+ private AtomicBoolean isTerminated = new AtomicBoolean(false);
+
+ // boolean indicating whether task to handle requests is running
+ private AtomicBoolean isTaskRunning = new AtomicBoolean(false);
+
+ public PaginationSubscription(Subscriber subscriber, AsyncPageFetcher nextPageFetcher) {
+ this.subscriber = subscriber;
+ this.nextPageFetcher = nextPageFetcher;
+ }
+
+ @Override
+ public void request(long n) {
+ if (isTerminated()) {
+ return;
+ }
+
+ if (n <= 0) {
+ throw new IllegalArgumentException("Non-positive request signals are illegal");
+ }
+
+ AtomicBoolean startTask = new AtomicBoolean(false);
+ synchronized (this) {
+ outstandingRequests.addAndGet(n);
+ startTask.set(startTask());
+ }
+
+ if (startTask.get()) {
+ handleRequests();
+ }
+ }
+
+ /**
+ * Recursive method to deal with requests until there are no outstandingRequests or
+ * no more pages.
+ */
+ protected abstract void handleRequests();
+
+ @Override
+ public void cancel() {
+ cleanup();
+ }
+
+ protected boolean hasNextPage() {
+ return currentPage == null || nextPageFetcher.hasNextPage(currentPage);
+ }
+
+ protected void completeSubscription() {
+ if (!isTerminated()) {
+ subscriber.onComplete();
+ cleanup();
+ }
+ }
+
+ private void terminate() {
+ isTerminated.compareAndSet(false, true);
+ }
+
+ protected boolean isTerminated() {
+ return isTerminated.get();
+ }
+
+ protected void stopTask() {
+ isTaskRunning.set(false);
+ }
+
+ private synchronized boolean startTask() {
+ return !isTerminated() && isTaskRunning.compareAndSet(false, true);
+ }
+
+ protected synchronized void cleanup() {
+ terminate();
+ stopTask();
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/ResponsesSubscription.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/ResponsesSubscription.java
new file mode 100644
index 000000000000..417d43e0eb65
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/ResponsesSubscription.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+/**
+ * An implementation of the {@link Subscription} interface that can be used to signal and cancel demand for
+ * paginated response pages.
+ *
+ * @param The type of a single response page
+ */
+public class ResponsesSubscription extends PaginationSubscription {
+
+ public ResponsesSubscription(Subscriber subscriber, AsyncPageFetcher nextPageFetcher) {
+ super(subscriber, nextPageFetcher);
+ }
+
+ protected void handleRequests() {
+ if (!hasNextPage()) {
+ completeSubscription();
+ return;
+ }
+
+ synchronized (this) {
+ if (outstandingRequests.get() <= 0) {
+ stopTask();
+ return;
+ }
+ }
+
+ if (!isTerminated()) {
+ outstandingRequests.getAndDecrement();
+ nextPageFetcher.nextPage(currentPage)
+ .whenComplete(((response, error) -> {
+ if (response != null) {
+ currentPage = response;
+ subscriber.onNext(response);
+ handleRequests();
+ }
+ if (error != null) {
+ subscriber.onError(error);
+ cleanup();
+ }
+ }));
+ }
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/SdkPublisher.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/SdkPublisher.java
new file mode 100644
index 000000000000..8ae681e9bf35
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/SdkPublisher.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import org.reactivestreams.Publisher;
+
+/**
+ * Interface that is implemented by the Async auto-paginated responses.
+ */
+public interface SdkPublisher extends Publisher {
+
+ // TODO Should we return the last response instead of Void?
+ default CompletableFuture forEach(Consumer consumer) {
+ CompletableFuture future = new CompletableFuture<>();
+ subscribe(new SequentialSubscriber<>(consumer, future));
+ return future;
+ }
+}
diff --git a/core/src/main/java/software/amazon/awssdk/core/pagination/async/SequentialSubscriber.java b/core/src/main/java/software/amazon/awssdk/core/pagination/async/SequentialSubscriber.java
new file mode 100644
index 000000000000..e6f53f5f76a8
--- /dev/null
+++ b/core/src/main/java/software/amazon/awssdk/core/pagination/async/SequentialSubscriber.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.core.pagination.async;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+
+/**
+ * A sample implementation of {@link Subscriber} that requests data one at a time.
+ *
+ * @param Type of data requested
+ */
+public class SequentialSubscriber implements Subscriber {
+
+ private final Consumer consumer;
+ private final CompletableFuture> future;
+ private Subscription subscription;
+
+ public SequentialSubscriber(Consumer consumer, CompletableFuture future) {
+ this.consumer = consumer;
+ this.future = future;
+ }
+
+ @Override
+ public void onSubscribe(Subscription subscription) {
+ this.subscription = subscription;
+ subscription.request(1);
+ }
+
+ @Override
+ public void onNext(T t) {
+ consumer.accept(t);
+ subscription.request(1);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ future.completeExceptionally(t);
+ }
+
+ @Override
+ public void onComplete() {
+ // TODO Update spotbugs version when new version is released and remove this filter the spotbugs-suppressions.xml file
+ // SpotBugs incorrectly reports NP_NONNULL_PARAM_VIOLATION when passing null. The fix is not released yet
+ // https://github.com/spotbugs/spotbugs/issues/484
+ future.complete(null);
+ }
+}
diff --git a/core/src/test/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterableTest.java b/core/src/test/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterableTest.java
index ebf4ed184d9c..d65477018aa4 100644
--- a/core/src/test/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterableTest.java
+++ b/core/src/test/java/software/amazon/awssdk/core/pagination/PaginatedItemsIterableTest.java
@@ -67,14 +67,18 @@ public void hasNext_ReturnsFalse_WhenItemsAndPagesIteratorHasNoNextElement() {
@Test
public void hasNext_ReturnsTrue_WhenItemsIteratorHasNextElement() {
+ when(pagesIterator.hasNext()).thenReturn(true);
+ when(getItemIteratorFunction.apply(any())).thenReturn(singlePageItemsIterator);
when(singlePageItemsIterator.hasNext()).thenReturn(true);
- when(pagesIterator.hasNext()).thenReturn(false);
assertTrue(itemsIterable.iterator().hasNext());
}
@Test
public void hasNextMethodDoesNotRetrieveNextPage_WhenItemsIteratorHasAnElement() {
+ when(pagesIterator.hasNext()).thenReturn(true);
+ when(getItemIteratorFunction.apply(any())).thenReturn(singlePageItemsIterator);
+
when(singlePageItemsIterator.hasNext()).thenReturn(true);
Iterator itemsIterator = itemsIterable.iterator();
@@ -102,12 +106,12 @@ public void hasNextMethodGetsNextPage_WhenCurrentItemsIteratorHasNoElements() {
}
@Test
- public void hasNextMethodGetsNextPage_WhenCurrentItemsIteratorIsNull() {
+ public void hasNextMethodGetsNextPage_WhenCurrentItemsIteratorIsEmpty() {
when(pagesIterator.hasNext()).thenReturn(true);
- when(getItemIteratorFunction.apply(any())).thenReturn(null, singlePageItemsIterator);
+ when(getItemIteratorFunction.apply(any())).thenReturn(singlePageItemsIterator);
- when(singlePageItemsIterator.hasNext()).thenReturn(true);
+ when(singlePageItemsIterator.hasNext()).thenReturn(false, true);
itemsIterable.iterator().hasNext();
diff --git a/pom.xml b/pom.xml
index d3119b4bb69c..ca6510c80f76 100644
--- a/pom.xml
+++ b/pom.xml
@@ -247,6 +247,11 @@
netty-reactive-streams-http
2.0.0
+
+ io.reactivex.rxjava2
+ rxjava
+ 2.1.9
+
diff --git a/services/dynamodb/src/it/java/software/amazon/awssdk/services/dynamodb/ScanPaginatorIntegrationTest.java b/services/dynamodb/src/it/java/software/amazon/awssdk/services/dynamodb/ScanPaginatorIntegrationTest.java
index b802f81e54f3..0ab7419ff0e3 100644
--- a/services/dynamodb/src/it/java/software/amazon/awssdk/services/dynamodb/ScanPaginatorIntegrationTest.java
+++ b/services/dynamodb/src/it/java/software/amazon/awssdk/services/dynamodb/ScanPaginatorIntegrationTest.java
@@ -15,12 +15,14 @@
package software.amazon.awssdk.services.dynamodb;
+import static org.junit.Assert.assertEquals;
+
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.stream.Stream;
import org.junit.AfterClass;
-import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import software.amazon.awssdk.core.pagination.SdkIterable;
@@ -35,7 +37,7 @@
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
-import software.amazon.awssdk.services.dynamodb.paginators.ScanPaginator;
+import software.amazon.awssdk.services.dynamodb.paginators.ScanIterable;
import utils.resources.tables.BasicTempTable;
import utils.test.util.DynamoDBTestBase;
import utils.test.util.TableUtils;
@@ -78,7 +80,7 @@ public static void cleanUpFixture() {
@Test
public void test_MultipleIteration_On_Responses_Iterable() {
ScanRequest request = ScanRequest.builder().tableName(TABLE_NAME).limit(2).build();
- ScanPaginator scanResponses = dynamo.scanIterable(request);
+ ScanIterable scanResponses = dynamo.scanPaginator(request);
int count = 0;
@@ -86,20 +88,20 @@ public void test_MultipleIteration_On_Responses_Iterable() {
for (ScanResponse response : scanResponses) {
count += response.count();
}
- Assert.assertEquals(ITEM_COUNT, count);
+ assertEquals(ITEM_COUNT, count);
// Iterate second time
count = 0;
for (ScanResponse response : scanResponses) {
count += response.count();
}
- Assert.assertEquals(ITEM_COUNT, count);
+ assertEquals(ITEM_COUNT, count);
}
@Test
public void test_MultipleIteration_On_PaginatedMember_Iterable() {
ScanRequest request = ScanRequest.builder().tableName(TABLE_NAME).limit(2).build();
- SdkIterable