All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.codegen.docs.PaginationDocs Maven / Gradle / Ivy

/*
 * Copyright 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.docs;

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.model.service.PaginatorDefinition;
import software.amazon.awssdk.codegen.poet.PoetExtension;
import software.amazon.awssdk.codegen.utils.PaginatorUtils;
import software.amazon.awssdk.utils.async.SequentialSubscriber;

public class PaginationDocs {

    private static final String SUBSCRIBE_METHOD_NAME = "subscribe";

    private final OperationModel operationModel;
    private final PoetExtension poetExtensions;
    private final PaginatorDefinition paginatorDefinition;

    public PaginationDocs(IntermediateModel intermediateModel, OperationModel operationModel,
                          PaginatorDefinition paginatorDefinition) {
        this.operationModel = operationModel;
        this.poetExtensions = new PoetExtension(intermediateModel);
        this.paginatorDefinition = paginatorDefinition;
    }

    /**
     * Constructs additional documentation on the client operation that is appended to the service documentation.
     *
     * TODO Add a link to our developer guide which will have more details and sample code. Write a blog post too.
     */
    public String getDocsForSyncOperation() {
        return CodeBlock.builder()
                        .add("

This is a variant of {@link #$L($T)} 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.\n

", operationModel.getMethodName(), requestType()) .add("

\nWhen 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.\n

") .add(getSyncCodeSnippets()) .build() .toString(); } /** * Constructs javadocs for the generated response classes in a paginated operation. * @param clientInterface A java poet {@link ClassName} type of the sync client interface */ public String getDocsForSyncResponseClass(ClassName clientInterface) { return CodeBlock.builder() .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, 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 " + "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.

") .add(getSyncCodeSnippets()) .build() .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(), getPaginatedMethodName()) .build(); return CodeBlock.builder() .add("\n\n

The following are few ways to iterate through the response pages:

") .add("1) Using a Stream") .add(buildCode(CodeBlock.builder() .add(callOperationOnClient) .addStatement("responses.stream().forEach(....)") .build())) .add("\n\n2) Using For loop") .add(buildCode(CodeBlock.builder() .add(callOperationOnClient) .beginControlFlow("for ($T response : responses)", syncResponsePageType()) .addStatement(" // do something") .endControlFlow() .build())) .add("\n\n3) Use iterator directly") .add(buildCode(CodeBlock.builder() .add(callOperationOnClient) .addStatement("responses.iterator().forEachRemaining(....)") .build())) .add(noteAboutLimitConfigurationMethod()) .add(noteAboutSyncNonPaginatedMethod()) .build() .toString(); } private String getAsyncCodeSnippets() { CodeBlock callOperationOnClient = CodeBlock.builder() .addStatement("$T publisher = client.$L(request)", asyncPaginatedResponseType(), getPaginatedMethodName()) .build(); return CodeBlock.builder() .add("\n\n

The following are few ways to use the response class:

") .add("1) Using the subscribe helper method", TypeName.get(SequentialSubscriber.class)) .add(buildCode(CodeBlock.builder() .add(callOperationOnClient) .add(CodeBlock.builder() .addStatement("CompletableFuture future = publisher" + ".subscribe(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(noteAboutLimitConfigurationMethod()) .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 getPaginatedMethodName() { return PaginatorUtils.getPaginatedMethodName(operationModel.getMethodName()); } /** * @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 return type of sync non-paginated operation. * * Example: For ListTables operation, it will be "ListTablesResponse" class. */ private ClassName syncResponsePageType() { return poetExtensions.getModelClass(operationModel.getReturnType().getReturnType()); } /** * @return A Poet {@link ClassName} for the return type of sync paginated operation. */ 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 String getPaginatorLimitKeyName() { return paginatorDefinition != null ? paginatorDefinition.getLimitKey() : ""; } private CodeBlock noteAboutLimitConfigurationMethod() { return CodeBlock.builder() .add("\n

Please notice that the configuration of $L won't limit the number of results " + "you get with the paginator. It only limits the number of results in each page.

", getPaginatorLimitKeyName()) .build(); } private CodeBlock noteAboutSyncNonPaginatedMethod() { return CodeBlock.builder() .add("\n

Note: 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); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy