From c7a4720ce41935dea23a5316299777c6f8963bd5 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Mon, 26 May 2025 09:23:27 +0500 Subject: [PATCH 01/33] feat(sync-pagination): adds page type, item type, exception handling --- src/main/java/io/apimatic/core/ApiCall.java | 52 +++- .../java/io/apimatic/core/HttpRequest.java | 37 +++ .../io/apimatic/core/ResponseHandler.java | 67 ++--- .../core/authentication/AuthBuilder.java | 14 + .../types/pagination/CursorPagination.java | 38 ++- .../core/types/pagination/LinkPagination.java | 30 +- .../types/pagination/OffsetPagination.java | 36 ++- .../core/types/pagination/PagePagination.java | 34 ++- .../core/types/pagination/PaginatedData.java | 267 ++++++++---------- .../pagination/PaginationDataManager.java | 17 -- .../pagination/PaginationDeserializer.java | 28 -- .../types/pagination/PaginationStrategy.java | 14 + .../apimatic/core/utilities/CoreHelper.java | 12 +- src/test/java/apimatic/core/EndToEndTest.java | 4 +- .../type/pagination/OffsetPaginationTest.java | 16 +- 15 files changed, 348 insertions(+), 318 deletions(-) delete mode 100644 src/main/java/io/apimatic/core/types/pagination/PaginationDataManager.java delete mode 100644 src/main/java/io/apimatic/core/types/pagination/PaginationDeserializer.java create mode 100644 src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java diff --git a/src/main/java/io/apimatic/core/ApiCall.java b/src/main/java/io/apimatic/core/ApiCall.java index daa1b4c2..b2fcdb59 100644 --- a/src/main/java/io/apimatic/core/ApiCall.java +++ b/src/main/java/io/apimatic/core/ApiCall.java @@ -1,13 +1,18 @@ package io.apimatic.core; import java.io.IOException; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import java.util.function.Function; import io.apimatic.core.configurations.http.request.EndpointConfiguration; import io.apimatic.core.logger.SdkLoggerFactory; import io.apimatic.core.request.async.AsyncExecutor; import io.apimatic.core.types.CoreApiException; +import io.apimatic.core.types.pagination.PageWrapper; +import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.core.types.pagination.PaginationStrategy; import io.apimatic.coreinterfaces.http.Context; import io.apimatic.coreinterfaces.http.request.Request; import io.apimatic.coreinterfaces.http.response.Response; @@ -45,6 +50,8 @@ public final class ApiCall * An instance of {@link ApiLogger} for logging. */ private final ApiLogger apiLogger; + + private Response response; /** @@ -64,6 +71,24 @@ private ApiCall(final GlobalConfiguration globalConfig, this.endpointConfiguration = endpointConfiguration; this.apiLogger = SdkLoggerFactory.getLogger(globalConfig.getLoggingConfiguration()); } + + public T paginate( + Function, T> converter, + Function, P> responseToPage, + Function> responseToItems, + PaginationStrategy... dataManagers) { + return converter.apply(new PaginatedData( + this, responseToPage, responseToItems, dataManagers + )); + } + + public Response getResponse() { + return response; + } + + public HttpRequest.Builder getRequestBuilder() { + return requestBuilder.copy(); + } /** * Execute the ApiCall and returns the expected response. @@ -74,7 +99,7 @@ private ApiCall(final GlobalConfiguration globalConfig, public ResponseType execute() throws IOException, ExceptionType { Request request = requestBuilder.build(globalConfig); apiLogger.logRequest(request); - Response response = globalConfig.getHttpClient().execute(request, endpointConfiguration); + response = globalConfig.getHttpClient().execute(request, endpointConfiguration); apiLogger.logResponse(response); Context context = globalConfig.getCompatibilityFactory() @@ -92,12 +117,24 @@ public CompletableFuture executeAsync() { request -> globalConfig.getHttpClient() .executeAsync(request, endpointConfiguration), (request, response) -> { + this.response = response; Context context = globalConfig.getCompatibilityFactory() .createHttpContext(request, response); return responseHandler.handle(context, endpointConfiguration, globalConfig, requestBuilder); }, apiLogger); } + + public Builder toBuilder() { + Builder builder = new Builder(); + + builder.globalConfig = globalConfig; + builder.endpointConfigurationBuilder = endpointConfiguration.toBuilder(); + builder.responseHandlerBuilder = responseHandler.toBuilder(); + builder.requestBuilder = requestBuilder.copy(); + + return builder; + } /** * Builder class for the {@link ApiCall} class. @@ -179,22 +216,11 @@ public Builder endpointConfiguration( return this; } - /** - * @param builder endpointConfigurationBuilder {@link EndpointConfiguration.Builder}. - * @return {@link ApiCall.Builder}. - */ - public Builder endpointConfiguration( - EndpointConfiguration.Builder builder) { - endpointConfigurationBuilder = builder; - return this; - } - /** * build the {@link ApiCall}. * @return the instance of {@link ApiCall}. - * @throws IOException Signals that an I/O exception of some sort has occurred. */ - public ApiCall build() throws IOException { + public ApiCall build() { return new ApiCall(globalConfig, endpointConfigurationBuilder.build(), requestBuilder, responseHandlerBuilder.build()); diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index b787bbf2..6d3891f1 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -598,6 +598,43 @@ private static String getSerializedHeaderValue(Object obj) { return CoreHelper.trySerialize(obj); } + + public String getQueryUrl() { + StringBuilder builder = new StringBuilder(path); + + CoreHelper.appendUrlWithQueryParameters(builder, queryParams, + arraySerializationFormat); + CoreHelper.appendUrlWithTemplateParameters(builder, templateParams); + + return builder.toString(); + } + + public Builder copy() { + Builder copy = new Builder(); + copy.server = this.server; + copy.path = this.path; + copy.httpMethod = this.httpMethod; + copy.authBuilder = this.authBuilder.copy(); // Ensure AuthBuilder has a copy() method. + copy.queryParams = new HashMap<>(this.queryParams); + for (Map.Entry> entry : this.templateParams.entrySet()) { + copy.templateParams.put(entry.getKey(), + new SimpleEntry<>(entry.getValue().getKey(), entry.getValue().getValue())); + } + for (Map.Entry> entry : this.headerParams.entrySet()) { + copy.headerParams.put(entry.getKey(), new ArrayList<>(entry.getValue())); + } + copy.formParams = new HashSet<>(this.formParams); + copy.formParamaters = new HashMap<>(this.formParamaters); + copy.body = this.body; + copy.bodySerializer = this.bodySerializer; + if (this.bodyParameters != null) { + copy.bodyParameters = new HashMap<>(this.bodyParameters); + } + copy.arraySerializationFormat = this.arraySerializationFormat; + copy.parameterBuilder = new Parameter.Builder(); + + return copy; + } /** * Initialise the CoreHttpRequest. diff --git a/src/main/java/io/apimatic/core/ResponseHandler.java b/src/main/java/io/apimatic/core/ResponseHandler.java index 95673042..5092c5a9 100644 --- a/src/main/java/io/apimatic/core/ResponseHandler.java +++ b/src/main/java/io/apimatic/core/ResponseHandler.java @@ -2,19 +2,12 @@ import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.fasterxml.jackson.core.type.TypeReference; - import io.apimatic.core.configurations.http.request.EndpointConfiguration; import io.apimatic.core.types.CoreApiException; -import io.apimatic.core.types.pagination.PaginatedData; -import io.apimatic.core.types.pagination.PaginationDeserializer; -import io.apimatic.core.types.pagination.PaginationDataManager; import io.apimatic.core.utilities.CoreHelper; import io.apimatic.coreinterfaces.compatibility.CompatibilityFactory; import io.apimatic.coreinterfaces.http.Context; @@ -59,13 +52,12 @@ public final class ResponseHandler deserializer; + private final Deserializer deserializer; /** - * A functional interface to wrap in pagination types - * {@link PaginationDeserializer}. + * An instance of {@link Deserializer}. */ - private final PaginationDeserializer paginationDeserializer; + private final Deserializer intermediateDeserializer; /** * An instance of {@link ResponseClassType}. @@ -102,14 +94,13 @@ private ResponseHandler(final Map> localErrorCa final Map> globalErrorCases, final Deserializer deserializer, final Deserializer intermediateDeserializer, - final PaginationDeserializer paginationDeserializer, final ResponseClassType responseClassType, final ContextInitializer contextInitializer, final boolean isNullify404Enabled, final boolean isNullableResponseType) { this.localErrorCases = localErrorCases; this.globalErrorCases = globalErrorCases; - this.deserializer = deserializer == null ? intermediateDeserializer : deserializer; - this.paginationDeserializer = paginationDeserializer; + this.deserializer = deserializer; + this.intermediateDeserializer = intermediateDeserializer; this.responseClassType = responseClassType; this.contextInitializer = contextInitializer; this.isNullify404Enabled = isNullify404Enabled; @@ -189,8 +180,8 @@ private Object convertResponse(Response response, HttpRequest.Builder requestBui return deserializer.apply(response.getBody()); } - if (paginationDeserializer != null) { - return paginationDeserializer.apply(config, globalConfig, requestBuilder, response); + if (intermediateDeserializer != null) { + return intermediateDeserializer.apply(response.getBody()); } return null; @@ -245,6 +236,21 @@ private void throwConfiguredException(Map> erro } } + public Builder toBuilder() { + Builder builder = new Builder() + .globalErrorCase(this.globalErrorCases != null ? new HashMap<>(this.globalErrorCases) : null) + .deserializer(this.deserializer) + .apiResponseDeserializer(this.intermediateDeserializer) + .responseClassType(this.responseClassType) + .contextInitializer(this.contextInitializer) + .nullify404(this.isNullify404Enabled) + .nullableResponseType(this.isNullableResponseType); + + builder.localErrorCases = localErrorCases; + + return builder; + } + public static class Builder { /** * A map of end point level errors. @@ -266,11 +272,6 @@ public static class Builder intermediateDeserializer; - /** - * A functional interface to wrap in pagination types - * {@link PaginationDeserializer}. - */ - private PaginationDeserializer paginationDeserializer; /** * An instance of {@link ResponseClassType}. @@ -348,28 +349,6 @@ public Builder deserializer( return this; } - /** - * Setter for the deserializer to be used in pagination wrapper. - * - * @param pageType TypeReference representing the type. - * @param converter Converter to convert Page into the list of InnerType. - * @param returnTypeGetter Converter to convert PaginatedData into the return type. - * @param dataManagers List of pagination data managers. - * @param the type of the outer page. - * @param the type wrapped by PaginatedIterable. - * - * @return {@link ResponseHandler.Builder}. - */ - public Builder paginatedDeserializer( - TypeReference pageType, Function> converter, - Function, ?> returnTypeGetter, - PaginationDataManager... dataManagers) { - this.paginationDeserializer = (config, globalConfig, reqBuilder, res) -> - returnTypeGetter.apply(new PaginatedData(config, globalConfig, - reqBuilder, res, pageType, converter, dataManagers)); - return this; - } - /** * Setter for the responseClassType. * @@ -425,7 +404,7 @@ public Builder nullableResponseType( public ResponseHandler build() { return new ResponseHandler(localErrorCases, globalErrorCases, deserializer, intermediateDeserializer, - paginationDeserializer, responseClassType, contextInitializer, + responseClassType, contextInitializer, isNullify404Enabled, isNullableResponseType); } } diff --git a/src/main/java/io/apimatic/core/authentication/AuthBuilder.java b/src/main/java/io/apimatic/core/authentication/AuthBuilder.java index 114190bc..69cb3b75 100644 --- a/src/main/java/io/apimatic/core/authentication/AuthBuilder.java +++ b/src/main/java/io/apimatic/core/authentication/AuthBuilder.java @@ -136,4 +136,18 @@ private List buildAuthGroup(Map authMana return auths; } + + public AuthBuilder copy() { + AuthBuilder copy = new AuthBuilder(); + copy.authKeys = new ArrayList<>(this.authKeys); + for (Map.Entry> entry : this.authBuilders.entrySet()) { + List copiedList = new ArrayList<>(); + for (AuthBuilder builder : entry.getValue()) { + copiedList.add(builder.copy()); + } + copy.authBuilders.put(entry.getKey(), copiedList); + } + + return copy; + } } diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index 41b6ae8a..520f8f24 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -2,11 +2,12 @@ import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.utilities.CoreHelper; +import io.apimatic.coreinterfaces.http.response.Response; -public class CursorPagination implements PaginationDataManager { +public class CursorPagination implements PaginationStrategy { private final String output; private final String input; - private Builder nextReqBuilder; + private String currentRequestCursor; /** * @param output JsonPointer of a field received in the response, representing next cursor. @@ -18,27 +19,36 @@ public CursorPagination(final String output, final String input) { } @Override - public boolean isValid(PaginatedData paginatedData) { - nextReqBuilder = paginatedData.getLastRequestBuilder(); + public Builder apply(PaginatedData paginatedData) { + Response response = paginatedData.getResponse(); + Builder reqBuilder = paginatedData.getRequestBuilder(); + final boolean[] isUpdated = {false}; - String cursorValue = CoreHelper.resolveResponsePointer(output, - paginatedData.getLastResponseBody(), paginatedData.getLastResponseHeaders()); + reqBuilder.updateByReference(input, old -> { + + if (response == null) { + currentRequestCursor = (String) old; + isUpdated[0] = true; + return old; + } - if (cursorValue == null) { - return false; - } + String cursorValue = CoreHelper.resolveResponsePointer(output, response); - final boolean[] isUpdated = {false}; - nextReqBuilder.updateByReference(input, old -> { + if (cursorValue == null) { + return old; + } + + currentRequestCursor = cursorValue; isUpdated[0] = true; return cursorValue; }); - return isUpdated[0]; + return isUpdated[0] ? reqBuilder : null; } @Override - public Builder getNextRequestBuilder() { - return nextReqBuilder; + public void addMetaData(PageWrapper page) { + page.setCursorInput(currentRequestCursor); + currentRequestCursor = null; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java index 4f6ed76d..475de44c 100644 --- a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java @@ -2,10 +2,11 @@ import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.utilities.CoreHelper; +import io.apimatic.coreinterfaces.http.response.Response; -public class LinkPagination implements PaginationDataManager { +public class LinkPagination implements PaginationStrategy { private final String next; - private Builder nextReqBuilder; + private String currentRequestLink; /** * @param next JsonPointer of a field in response, representing next request queryUrl. @@ -15,23 +16,28 @@ public LinkPagination(final String next) { } @Override - public boolean isValid(PaginatedData paginatedData) { - nextReqBuilder = paginatedData.getLastRequestBuilder(); + public Builder apply(PaginatedData paginatedData) { + Response response = paginatedData.getResponse(); - String linkValue = CoreHelper.resolveResponsePointer(next, - paginatedData.getLastResponseBody(), paginatedData.getLastResponseHeaders()); + if (response == null) { + currentRequestLink = paginatedData.getRequestBuilder().getQueryUrl(); + return paginatedData.getRequestBuilder(); + } + + String linkValue = CoreHelper.resolveResponsePointer(next, response); if (linkValue == null) { - return false; + return null; } + currentRequestLink = linkValue; - nextReqBuilder.queryParam(CoreHelper.getQueryParameters(linkValue)); - - return true; + return paginatedData.getRequestBuilder() + .queryParam(CoreHelper.getQueryParameters(linkValue)); } @Override - public Builder getNextRequestBuilder() { - return nextReqBuilder; + public void addMetaData(PageWrapper page) { + page.setNextLinkInput(currentRequestLink); + currentRequestLink = null; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index 6a14791d..4b36c7ed 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -1,10 +1,11 @@ package io.apimatic.core.types.pagination; import io.apimatic.core.HttpRequest.Builder; +import io.apimatic.coreinterfaces.http.response.Response; -public class OffsetPagination implements PaginationDataManager { +public class OffsetPagination implements PaginationStrategy { private final String input; - private Builder nextReqBuilder; + private int currentRequestOffset = -1; /** * @param input JsonPointer of a field in request, representing offset. @@ -14,25 +15,32 @@ public OffsetPagination(final String input) { } @Override - public boolean isValid(PaginatedData paginatedData) { - nextReqBuilder = paginatedData.getLastRequestBuilder(); - - if (input == null) { - return false; - } - + public Builder apply(PaginatedData paginatedData) { + Response response = paginatedData.getResponse(); + Builder reqBuilder = paginatedData.getRequestBuilder(); final boolean[] isUpdated = {false}; - nextReqBuilder.updateByReference(input, old -> { - int newValue = Integer.parseInt("" + old) + paginatedData.getLastDataSize(); + + reqBuilder.updateByReference(input, old -> { + int oldValue = Integer.parseInt("" + old); + + if (response == null) { + currentRequestOffset = oldValue; + isUpdated[0] = true; + return old; + } + + int newValue = oldValue + paginatedData.getLastPageSize(); + currentRequestOffset = newValue; isUpdated[0] = true; return newValue; }); - return isUpdated[0]; + return isUpdated[0] ? reqBuilder : null; } @Override - public Builder getNextRequestBuilder() { - return nextReqBuilder; + public void addMetaData(PageWrapper page) { + page.setOffsetInput(currentRequestOffset); + currentRequestOffset = -1; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index 2e1f6163..51ce130e 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -1,10 +1,11 @@ package io.apimatic.core.types.pagination; import io.apimatic.core.HttpRequest.Builder; +import io.apimatic.coreinterfaces.http.response.Response; -public class PagePagination implements PaginationDataManager { +public class PagePagination implements PaginationStrategy { private final String input; - private Builder nextReqBuilder; + private int currentRequestPageNumber = -1; /** * @param input JsonPointer of a field in request, representing page. @@ -14,25 +15,32 @@ public PagePagination(final String input) { } @Override - public boolean isValid(PaginatedData paginatedData) { - nextReqBuilder = paginatedData.getLastRequestBuilder(); + public Builder apply(PaginatedData paginatedData) { + Response response = paginatedData.getResponse(); + Builder reqBuilder = paginatedData.getRequestBuilder(); + final boolean[] isUpdated = {false}; - if (input == null) { - return false; - } + reqBuilder.updateByReference(input, old -> { + int oldValue = Integer.parseInt("" + old); - final boolean[] isUpdated = {false}; - nextReqBuilder.updateByReference(input, old -> { - int newValue = Integer.parseInt("" + old) + 1; + if (response == null) { + currentRequestPageNumber = oldValue; + isUpdated[0] = true; + return old; + } + + int newValue = oldValue + 1; + currentRequestPageNumber = newValue; isUpdated[0] = true; return newValue; }); - return isUpdated[0]; + return isUpdated[0] ? reqBuilder : null; } @Override - public Builder getNextRequestBuilder() { - return nextReqBuilder; + public void addMetaData(PageWrapper page) { + page.setPageInput(currentRequestPageNumber); + currentRequestPageNumber = -1; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index e0024121..704ba104 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -2,149 +2,77 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.function.Function; - -import com.fasterxml.jackson.core.type.TypeReference; +import java.util.stream.Collectors; import io.apimatic.core.ApiCall; -import io.apimatic.core.ErrorCase; -import io.apimatic.core.GlobalConfiguration; import io.apimatic.core.HttpRequest; -import io.apimatic.core.configurations.http.request.EndpointConfiguration; import io.apimatic.core.types.CoreApiException; -import io.apimatic.core.utilities.CoreHelper; import io.apimatic.coreinterfaces.http.response.Response; -public class PaginatedData implements Iterator { +public class PaginatedData implements Iterator> { + private final ApiCall firstApiCall; + private final Function, P> responseToPage; + private final Function> responseToItems; + private final PaginationStrategy[] strategies; + private int currentIndex = 0; - - private final List data = new ArrayList(); - private final List

pages = new ArrayList

(); - private int lastDataSize; - private Response lastResponse; - private HttpRequest.Builder lastRequestBuilder; - - private final TypeReference

pageType; - private final Function> converter; - private final PaginationDataManager[] dataManagers; - private final EndpointConfiguration endpointConfig; - private final GlobalConfiguration globalConfig; - - /** - * @param paginatedData Existing instance to be cloned. - */ - public PaginatedData(final PaginatedData paginatedData) { - this.pageType = paginatedData.pageType; - this.converter = paginatedData.converter; - this.dataManagers = paginatedData.dataManagers; - this.endpointConfig = paginatedData.endpointConfig; - this.globalConfig = paginatedData.globalConfig; - - this.lastDataSize = paginatedData.lastDataSize; - this.lastResponse = paginatedData.lastResponse; - this.lastRequestBuilder = paginatedData.lastRequestBuilder; - - this.data.addAll(paginatedData.data); - this.pages.addAll(paginatedData.pages); - } - - /** - * @param config ApiCall configuration that provided this paginated data. - * @param globalConfig Global configuration that provided this paginated data. - * @param requestBuilder RequestBuilder that provided this paginated data. - * @param response Response corresponding to this paginated data instance. - * @param pageType TypeReference of page type P. - * @param converter PageType P to list of ItemType T converter - * @param dataManagers A list of data managers that provided this paginated data. - * - * @throws IOException - */ - public PaginatedData(final EndpointConfiguration config, final GlobalConfiguration globalConfig, - final HttpRequest.Builder requestBuilder, final Response response, - final TypeReference

pageType, final Function> converter, - final PaginationDataManager... dataManagers) throws IOException { - this.pageType = pageType; - this.converter = converter; - this.dataManagers = dataManagers; - this.endpointConfig = config; - this.globalConfig = globalConfig; - - updateUsing(response, requestBuilder); - } - - private void updateUsing(Response response, HttpRequest.Builder requestBuilder) - throws IOException { - String responseBody = response.getBody(); - P page = CoreHelper.deserialize(responseBody, pageType); - List newData = converter.apply(page); - - this.lastDataSize = newData.size(); - this.lastResponse = response; - this.lastRequestBuilder = requestBuilder; - - this.data.addAll(newData); - this.pages.add(page); + private List> items = new ArrayList<>(); + private PageWrapper page = null; + private int lastPageSize = 0; + private ApiCall apiCall; + private boolean caughtFailure = false; + + public PaginatedData(final ApiCall apiCall, + final Function, P> responseToPage, + final Function> responseToItems, + final PaginationStrategy... dataManagers) { + this.firstApiCall = apiCall; + this.responseToPage = responseToPage; + this.responseToItems = responseToItems; + this.strategies = dataManagers; + this.apiCall = apiCall; } /** * @return RequestBuilder that provided the last page */ - public HttpRequest.Builder getLastRequestBuilder() { - return lastRequestBuilder; - } - - /** - * @return Response body corresponding to the last page - */ - public String getLastResponseBody() { - return lastResponse.getBody(); + public HttpRequest.Builder getRequestBuilder() { + return apiCall.getRequestBuilder(); } /** - * @return Response headers corresponding to the last page + * @return Response corresponding to the last page */ - public String getLastResponseHeaders() { - return CoreHelper.trySerialize(lastResponse.getHeaders().asSimpleMap()); + public Response getResponse() { + return apiCall.getResponse(); } /** * @return Size of the last page */ - public int getLastDataSize() { - return lastDataSize; - } - - /** - * @return Reset this instance and return a clone if its traversed before - */ - public PaginatedData reset() { - if (currentIndex == 0) { - return this; - } - - return new PaginatedData(this); + public int getLastPageSize() { + return lastPageSize; } @Override public boolean hasNext() { - if (currentIndex < data.size()) { + if (currentIndex < items.size()) { return true; } - fetchMoreData(); - - return currentIndex < data.size(); + return fetchNextPage(); } @Override - public T next() { + public ItemWrapper next() { if (hasNext()) { - return data.get(currentIndex++); + return items.get(currentIndex++); } throw new NoSuchElementException("No more data available."); @@ -153,74 +81,119 @@ public T next() { /** * @return An iterable of items of type T */ - public Iterator iterator() { - return reset(); + public Iterator iterator(Function, T> converter) { + PaginatedData paginatedData = new PaginatedData<>( + firstApiCall, responseToPage, responseToItems, strategies); + + return new Iterator() { + + @Override + public boolean hasNext() { + return paginatedData.hasNext(); + } + + @Override + public T next() { + return converter.apply(paginatedData.next()); + } + }; } /** * @return An iterable of pages of type P */ public Iterable

pages() { - PaginatedData dataCopy = reset(); + PaginatedData paginatedData = new PaginatedData<>( + firstApiCall, responseToPage, responseToItems, strategies); return new Iterable

() { @Override public Iterator

iterator() { return new Iterator

() { - private int currentIndex = 0; - @Override public boolean hasNext() { - if (currentIndex < dataCopy.pages.size()) { - return true; - } - - while (dataCopy.hasNext()) { - if (currentIndex < dataCopy.pages.size()) { - return true; - } - dataCopy.next(); - } - - return false; + return paginatedData.fetchNextPage(); } @Override public P next() { - if (dataCopy.hasNext()) { - return dataCopy.pages.get(currentIndex++); - } - - throw new NoSuchElementException("No more data available."); + return responseToPage.apply(paginatedData.page); } }; } }; } - private void fetchMoreData() { - for (PaginationDataManager manager : dataManagers) { + private boolean fetchNextPage() { + if (caughtFailure) { + return false; + } + + for (PaginationStrategy strategy : strategies) { - if (!manager.isValid(this)) { + HttpRequest.Builder requestBuilder = strategy.apply(this); + if (requestBuilder == null) { continue; } + + boolean isUpdated = tryUpdatingPage(requestBuilder); + if (isUpdated) { + strategy.addMetaData(page); + } + + return isUpdated; + } + return false; + } - try { - PaginatedData result = - new ApiCall.Builder, CoreApiException>() - .endpointConfiguration(endpointConfig.toBuilder()) - .globalConfig(globalConfig) - .requestBuilder(manager.getNextRequestBuilder()) - .responseHandler(res -> res - .globalErrorCase(Collections.singletonMap(ErrorCase.DEFAULT, - ErrorCase.setReason(null, CoreApiException::new))) - .nullify404(false) - .paginatedDeserializer(pageType, converter, r -> r, dataManagers)) - .build().execute(); - - updateUsing(result.lastResponse, result.lastRequestBuilder); - return; - } catch (Exception ignored) { + private boolean tryUpdatingPage(HttpRequest.Builder requestBuilder) { + ApiCall apiCall = this.apiCall.toBuilder() + .requestBuilder(requestBuilder).build(); + PageWrapper page; + List> items; + + try { + Res pageUnWrapped = apiCall.execute(); + if (pageUnWrapped == null) { + return false; + } + List itemsUnWrapped = responseToItems.apply(pageUnWrapped); + if (itemsUnWrapped == null) { + return false; } + page = new PageWrapper<>(apiCall.getResponse().getStatusCode(), + apiCall.getResponse().getHeaders(), pageUnWrapped, itemsUnWrapped); + items = itemsUnWrapped.stream().map(i -> new ItemWrapper() { + @Override + public I get() throws ExceptionType, IOException { + return i; + } + }).collect(Collectors.toList()); + } catch (IOException | CoreApiException exp) { + caughtFailure = true; + page = PageWrapper.CreateError(exp); + items = Arrays.asList(new ItemWrapper() { + @Override + public I get() throws ExceptionType, IOException { + throw exp; + } + }); + } + + return updateWith(apiCall, page, items); + } + + private boolean updateWith(ApiCall apiCall, + PageWrapper page, List> items) { + if (items.size() == 0) { + return false; } + + currentIndex = 0; + this.apiCall = apiCall; + this.page = page; + this.items = items; + lastPageSize = items.size(); + + return true; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginationDataManager.java b/src/main/java/io/apimatic/core/types/pagination/PaginationDataManager.java deleted file mode 100644 index 577c96eb..00000000 --- a/src/main/java/io/apimatic/core/types/pagination/PaginationDataManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.apimatic.core.types.pagination; - -import io.apimatic.core.HttpRequest.Builder; - -public interface PaginationDataManager { - - /** - * @param paginatedData Data to be checked for validity - * @return True if paginated data is valid to make another request - */ - boolean isValid(PaginatedData paginatedData); - - /** - * @return RequestBuilder for the next API Call - */ - Builder getNextRequestBuilder(); -} diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginationDeserializer.java b/src/main/java/io/apimatic/core/types/pagination/PaginationDeserializer.java deleted file mode 100644 index 81390625..00000000 --- a/src/main/java/io/apimatic/core/types/pagination/PaginationDeserializer.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.apimatic.core.types.pagination; - -import java.io.IOException; - -import io.apimatic.core.GlobalConfiguration; -import io.apimatic.core.HttpRequest; -import io.apimatic.core.configurations.http.request.EndpointConfiguration; -import io.apimatic.coreinterfaces.http.response.Response; - -/** - * Functional Interface to apply the deserializer function. - */ -@FunctionalInterface -public interface PaginationDeserializer { - /** - * Apply the deserialization function and returns the ResponseType response. - * - * @param config The EndPoint configuration for paginated API Calls. - * @param globalConfig The EndPoint configuration for paginated API Calls. - * @param requestBuilder The requestBuilder to re create current API Call. - * @param response The response of current API Call. - * - * @return The deserialized data. - * @throws IOException Exception to be thrown while applying the function. - */ - Object apply(EndpointConfiguration config, GlobalConfiguration globalConfig, - HttpRequest.Builder requestBuilder, Response response) throws IOException; -} diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java new file mode 100644 index 00000000..baa5ab09 --- /dev/null +++ b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java @@ -0,0 +1,14 @@ +package io.apimatic.core.types.pagination; + +import io.apimatic.core.HttpRequest; + +public interface PaginationStrategy { + + /** + * @param paginatedData Data to be checked for validity + * @return True if paginated data is valid to make another request + */ + HttpRequest.Builder apply(PaginatedData paginatedData); + + void addMetaData(PageWrapper page); +} diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index c268f602..66068baf 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -75,6 +75,7 @@ import io.apimatic.core.types.http.request.MultipartFileWrapper; import io.apimatic.core.types.http.request.MultipartWrapper; import io.apimatic.coreinterfaces.http.request.ArraySerializationFormat; +import io.apimatic.coreinterfaces.http.response.Response; /** * This is a Helper class with commonly used utilities for the SDK. @@ -1627,12 +1628,10 @@ public static JsonStructure createJsonStructure(String json) { * Resolves a pointer within a JSON response body or headers. * * @param pointer The JSON pointer. - * @param jsonBody The response body. - * @param jsonHeaders The response headers. + * @param response The response. * @return The resolved value as a string, or null if not found. */ - public static String resolveResponsePointer(String pointer, String jsonBody, - String jsonHeaders) { + public static String resolveResponsePointer(String pointer, Response response) { if (pointer == null) { return null; } @@ -1643,9 +1642,10 @@ public static String resolveResponsePointer(String pointer, String jsonBody, switch (prefix) { case "$response.body": - return CoreHelper.getValueFromJson(point, jsonBody); + return CoreHelper.getValueFromJson(point, response.getBody()); case "$response.headers": - return CoreHelper.getValueFromJson(point, jsonHeaders); + return CoreHelper.getValueFromJson(point, + trySerialize(response.getHeaders().asSimpleMap())); default: return null; } diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index 8fa775a6..989bb523 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -38,7 +38,7 @@ import io.apimatic.core.types.pagination.OffsetPagination; import io.apimatic.core.types.pagination.PagePagination; import io.apimatic.core.types.pagination.PaginatedData; -import io.apimatic.core.types.pagination.PaginationDataManager; +import io.apimatic.core.types.pagination.PaginationStrategy; import io.apimatic.core.utilities.CoreHelper; import io.apimatic.coreinterfaces.http.Callback; import io.apimatic.coreinterfaces.http.Context; @@ -520,7 +520,7 @@ private ApiCall getApiCall() throws IOException { } private ApiCall, CoreApiException> getPaginatedApiCall( - PaginationDataManager... pagination) throws IOException { + PaginationStrategy... pagination) throws IOException { when(response.getBody()).thenReturn("{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\",\"next_link\":\"https://localhost:3000/path?page=2\"}"); when(response.getHeaders()).thenReturn(getHttpHeaders()); diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index b8e32b86..c75f6d0b 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -43,7 +43,7 @@ public void testValidOffsetHeaderReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); @@ -63,7 +63,7 @@ public void testValidOffsetTemplateReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.path#/offset"); @@ -83,7 +83,7 @@ public void testValidOffsetReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -108,7 +108,7 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { put("val", "1"); } }))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); @@ -128,7 +128,7 @@ public void testValidStringOffsetReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value("5"))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -148,7 +148,7 @@ public void testInvalidStringOffsetReturnsFalse() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value("5a"))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -166,7 +166,7 @@ public void testMissingOffsetReturnsFalse() { PaginatedData paginatedData = mock(PaginatedData.class); when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -186,7 +186,7 @@ public void testNullOffsetReturnsFalse() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value(NUMERIC_OFFSET))); - when(paginatedData.getLastDataSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination(null); From e2198dc53b7eba841cba23490983950ab0a1428b Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Mon, 26 May 2025 09:24:21 +0500 Subject: [PATCH 02/33] pagination types for item and page wrapper --- .../core/types/pagination/ItemWrapper.java | 9 + .../core/types/pagination/PageWrapper.java | 154 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java create mode 100644 src/main/java/io/apimatic/core/types/pagination/PageWrapper.java diff --git a/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java b/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java new file mode 100644 index 00000000..d8db100e --- /dev/null +++ b/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java @@ -0,0 +1,9 @@ +package io.apimatic.core.types.pagination; + +import java.io.IOException; + +import io.apimatic.core.types.CoreApiException; + +public interface ItemWrapper { + T get() throws ExceptionType, IOException; +} diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java new file mode 100644 index 00000000..98011289 --- /dev/null +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -0,0 +1,154 @@ +package io.apimatic.core.types.pagination; + +import java.io.IOException; +import java.util.List; + +import io.apimatic.core.types.CoreApiException; +import io.apimatic.coreinterfaces.http.HttpHeaders; +public class PageWrapper { + + @SuppressWarnings("unchecked") + public static PageWrapper CreateError( + Exception exception) { + if (exception instanceof CoreApiException) { + return new PageWrapper((E) exception); + } + + if (exception instanceof IOException) { + return new PageWrapper((IOException) exception); + } + + return null; + } + + private final int statusCode; + private final HttpHeaders headers; + private P result; + private List items; + private E apiException = null; + private IOException ioException = null; + + private String nextLinkInput = null; + private int offsetInput = -1; + private int pageInput = -1; + private String cursorInput = null; + + public PageWrapper(int statusCode, HttpHeaders headers, P result, List items) { + this.statusCode = statusCode; + this.headers = headers; + this.result = result; + this.items = items; + } + + private PageWrapper(E apiException) { + this.statusCode = apiException.getResponseCode(); + this.headers = apiException.getHttpContext().getResponse().getHeaders(); + this.apiException = apiException; + } + + private PageWrapper(IOException ioException) { + this.statusCode = 0; + this.headers = null; + this.ioException = ioException; + } + + public void setNextLinkInput(String nextLinkInput) { + this.nextLinkInput = nextLinkInput; + } + + public void setOffsetInput(int offsetInput) { + this.offsetInput = offsetInput; + } + + public void setPageInput(int pageInput) { + this.pageInput = pageInput; + } + + public void setCursorInput(String cursorInput) { + this.cursorInput = cursorInput; + } + + /** + * Gets the next link input used for link-based pagination. + * @return The next page link + */ + public String getNextLinkInput() { + return nextLinkInput; + } + + /** + * Gets the offset input used for offset-based pagination. + * @return The offset value + */ + public int getOffsetInput() { + return offsetInput; + } + + /** + * Gets the page number input used for page-based pagination. + * @return The page number + */ + public int getPageInput() { + return pageInput; + } + + /** + * Gets the cursor input used for cursor-based pagination. + * @return The cursor token + */ + public String getCursorInput() { + return cursorInput; + } + + /** + * HTTP Status code of the api response. + * @return Int status code + */ + public int getStatusCode() { + return statusCode; + } + + /** + * Headers of the http response. + * @return Headers + */ + public HttpHeaders getHeaders() { + return headers; + } + + /** + * Content of the page. + * @return List of items on this page. + * @throws E + * @throws IOException + */ + public List getItems() throws E, IOException { + if (ioException != null) { + throw ioException; + } + + if (apiException != null) { + throw apiException; + } + + return items; + } + + /** + * Content of the page. + * @return Content + * @throws E + * @throws IOException + */ + public P getResult() throws E, IOException { + if (ioException != null) { + throw ioException; + } + + if (apiException != null) { + throw apiException; + } + + return result; + } +} From ffd3db2a5bce9f0531ecedbf1c777b44b257ea45 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 29 May 2025 10:58:15 +0500 Subject: [PATCH 03/33] adds async paginated data fetching logic --- src/main/java/io/apimatic/core/ApiCall.java | 8 +- .../types/pagination/CheckedSupplier.java | 47 ++++ .../types/pagination/CursorPagination.java | 2 +- .../core/types/pagination/ItemWrapper.java | 9 - .../core/types/pagination/LinkPagination.java | 2 +- .../types/pagination/OffsetPagination.java | 4 +- .../core/types/pagination/PagePagination.java | 2 +- .../core/types/pagination/PageWrapper.java | 75 ++----- .../core/types/pagination/PaginatedData.java | 211 +++++++++--------- .../types/pagination/PaginationStrategy.java | 2 +- .../type/pagination/OffsetPaginationTest.java | 16 +- 11 files changed, 189 insertions(+), 189 deletions(-) create mode 100644 src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java delete mode 100644 src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java diff --git a/src/main/java/io/apimatic/core/ApiCall.java b/src/main/java/io/apimatic/core/ApiCall.java index b2fcdb59..c502b23f 100644 --- a/src/main/java/io/apimatic/core/ApiCall.java +++ b/src/main/java/io/apimatic/core/ApiCall.java @@ -72,13 +72,13 @@ private ApiCall(final GlobalConfiguration globalConfig, this.apiLogger = SdkLoggerFactory.getLogger(globalConfig.getLoggingConfiguration()); } - public T paginate( + public T paginate( Function, T> converter, - Function, P> responseToPage, + Function, P> responseToPage, Function> responseToItems, - PaginationStrategy... dataManagers) { + PaginationStrategy... strategies) { return converter.apply(new PaginatedData( - this, responseToPage, responseToItems, dataManagers + this, responseToPage, responseToItems, strategies )); } diff --git a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java new file mode 100644 index 00000000..e41b9c39 --- /dev/null +++ b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java @@ -0,0 +1,47 @@ +package io.apimatic.core.types.pagination; + +import java.io.IOException; + +import io.apimatic.core.types.CoreApiException; + +public interface CheckedSupplier { + + @SuppressWarnings("unchecked") + public static CheckedSupplier CreateError( + Throwable exception) { + if (exception instanceof IOException) { + return new CheckedSupplier() + { + @Override + public T get() throws E, IOException { + throw (IOException) exception; + } + }; + } + + if (exception instanceof CoreApiException) { + return new CheckedSupplier() + { + @Override + public T get() throws E, IOException { + throw (E) exception; + } + }; + } + + return null; + + } + + public static CheckedSupplier Create(T item) { + return new CheckedSupplier() + { + @Override + public T get() throws E, IOException { + return item; + } + }; + } + + T get() throws ExceptionType, IOException; +} diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index 520f8f24..fd40b25d 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -47,7 +47,7 @@ public Builder apply(PaginatedData paginatedData) { } @Override - public void addMetaData(PageWrapper page) { + public void addMetaData(PageWrapper page) { page.setCursorInput(currentRequestCursor); currentRequestCursor = null; } diff --git a/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java b/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java deleted file mode 100644 index d8db100e..00000000 --- a/src/main/java/io/apimatic/core/types/pagination/ItemWrapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.apimatic.core.types.pagination; - -import java.io.IOException; - -import io.apimatic.core.types.CoreApiException; - -public interface ItemWrapper { - T get() throws ExceptionType, IOException; -} diff --git a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java index 475de44c..6978a622 100644 --- a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java @@ -36,7 +36,7 @@ public Builder apply(PaginatedData paginatedData) { } @Override - public void addMetaData(PageWrapper page) { + public void addMetaData(PageWrapper page) { page.setNextLinkInput(currentRequestLink); currentRequestLink = null; } diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index 4b36c7ed..273fb955 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -29,7 +29,7 @@ public Builder apply(PaginatedData paginatedData) { return old; } - int newValue = oldValue + paginatedData.getLastPageSize(); + int newValue = oldValue + paginatedData.getPageSize(); currentRequestOffset = newValue; isUpdated[0] = true; return newValue; @@ -39,7 +39,7 @@ public Builder apply(PaginatedData paginatedData) { } @Override - public void addMetaData(PageWrapper page) { + public void addMetaData(PageWrapper page) { page.setOffsetInput(currentRequestOffset); currentRequestOffset = -1; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index 51ce130e..4efd0f26 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -39,7 +39,7 @@ public Builder apply(PaginatedData paginatedData) { } @Override - public void addMetaData(PageWrapper page) { + public void addMetaData(PageWrapper page) { page.setPageInput(currentRequestPageNumber); currentRequestPageNumber = -1; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 98011289..09db28ed 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -3,55 +3,34 @@ import java.io.IOException; import java.util.List; -import io.apimatic.core.types.CoreApiException; import io.apimatic.coreinterfaces.http.HttpHeaders; -public class PageWrapper { - - @SuppressWarnings("unchecked") - public static PageWrapper CreateError( - Exception exception) { - if (exception instanceof CoreApiException) { - return new PageWrapper((E) exception); - } - - if (exception instanceof IOException) { - return new PageWrapper((IOException) exception); - } - - return null; +import io.apimatic.coreinterfaces.http.response.ApiResponseType; +import io.apimatic.coreinterfaces.http.response.Response; + +public class PageWrapper implements ApiResponseType

{ + + public static PageWrapper Create( + Response response, P page, List items) { + return new PageWrapper(response.getStatusCode(), response.getHeaders(), page, items); } - + private final int statusCode; private final HttpHeaders headers; - private P result; + private P page; private List items; - private E apiException = null; - private IOException ioException = null; - + private String nextLinkInput = null; private int offsetInput = -1; private int pageInput = -1; private String cursorInput = null; - public PageWrapper(int statusCode, HttpHeaders headers, P result, List items) { + private PageWrapper(int statusCode, HttpHeaders headers, P page, List items) { this.statusCode = statusCode; this.headers = headers; - this.result = result; + this.page = page; this.items = items; } - private PageWrapper(E apiException) { - this.statusCode = apiException.getResponseCode(); - this.headers = apiException.getHttpContext().getResponse().getHeaders(); - this.apiException = apiException; - } - - private PageWrapper(IOException ioException) { - this.statusCode = 0; - this.headers = null; - this.ioException = ioException; - } - public void setNextLinkInput(String nextLinkInput) { this.nextLinkInput = nextLinkInput; } @@ -118,37 +97,21 @@ public HttpHeaders getHeaders() { /** * Content of the page. - * @return List of items on this page. + * @return Content * @throws E * @throws IOException */ - public List getItems() throws E, IOException { - if (ioException != null) { - throw ioException; - } - - if (apiException != null) { - throw apiException; - } - - return items; + public P getResult() { + return page; } /** * Content of the page. - * @return Content + * @return List of items on this page. * @throws E * @throws IOException */ - public P getResult() throws E, IOException { - if (ioException != null) { - throw ioException; - } - - if (apiException != null) { - throw apiException; - } - - return result; + public List getItems() { + return items; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 704ba104..dcb2d725 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -2,129 +2,145 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.stream.Collectors; import io.apimatic.core.ApiCall; import io.apimatic.core.HttpRequest; import io.apimatic.core.types.CoreApiException; import io.apimatic.coreinterfaces.http.response.Response; -public class PaginatedData implements Iterator> { +public class PaginatedData { private final ApiCall firstApiCall; - private final Function, P> responseToPage; - private final Function> responseToItems; + private final Function, P> pageCreator; + private final Function> itemsCreator; private final PaginationStrategy[] strategies; - private int currentIndex = 0; - private List> items = new ArrayList<>(); - private PageWrapper page = null; - private int lastPageSize = 0; + private int itemIndex = 0; + private final List> items = new ArrayList<>(); + private CheckedSupplier page = null; private ApiCall apiCall; - private boolean caughtFailure = false; + private boolean dataClosed = false; public PaginatedData(final ApiCall apiCall, - final Function, P> responseToPage, - final Function> responseToItems, - final PaginationStrategy... dataManagers) { + final Function, P> pageCreator, + final Function> itemsCreator, + final PaginationStrategy... strategies) { this.firstApiCall = apiCall; - this.responseToPage = responseToPage; - this.responseToItems = responseToItems; - this.strategies = dataManagers; + this.pageCreator = pageCreator; + this.itemsCreator = itemsCreator; + this.strategies = strategies; this.apiCall = apiCall; } /** - * @return RequestBuilder that provided the last page + * @return RequestBuilder that provided this page */ public HttpRequest.Builder getRequestBuilder() { return apiCall.getRequestBuilder(); } /** - * @return Response corresponding to the last page + * @return Response corresponding to this page */ public Response getResponse() { return apiCall.getResponse(); } /** - * @return Size of the last page + * @return Size of this page */ - public int getLastPageSize() { - return lastPageSize; + public int getPageSize() { + return items.size(); } - @Override - public boolean hasNext() { - if (currentIndex < items.size()) { - return true; + public List getItems(Function, T> itemCreator) { + List items = new ArrayList<>(); + for (CheckedSupplier i : this.items) { + items.add(itemCreator.apply(i)); } - - return fetchNextPage(); + + return items; } - @Override - public ItemWrapper next() { - if (hasNext()) { - return items.get(currentIndex++); + public T getPage(Function, T> pageSupplier) { + if (page == null) { + return null; } - throw new NoSuchElementException("No more data available."); + return pageSupplier.apply(page); } /** - * @return An iterable of items of type T + * @return An Iterator of items of type T */ - public Iterator iterator(Function, T> converter) { + public Iterator items(Function, T> itemSupplier) { PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, responseToPage, responseToItems, strategies); - - return new Iterator() { + firstApiCall, pageCreator, itemsCreator, strategies); + return new Iterator() { @Override public boolean hasNext() { - return paginatedData.hasNext(); + if (paginatedData.itemIndex < paginatedData.items.size()) { + return true; + } + + return paginatedData.fetchNextPage(); } @Override public T next() { - return converter.apply(paginatedData.next()); + return itemSupplier.apply(paginatedData.items.get(paginatedData.itemIndex++)); } }; } /** - * @return An iterable of pages of type P + * @return An Iterator of pages of type T */ - public Iterable

pages() { + public Iterator pages(Function, T> pageSupplier) { PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, responseToPage, responseToItems, strategies); - return new Iterable

() { + firstApiCall, pageCreator, itemsCreator, strategies); + + return new Iterator() { @Override - public Iterator

iterator() { - return new Iterator

() { - @Override - public boolean hasNext() { - return paginatedData.fetchNextPage(); - } - - @Override - public P next() { - return responseToPage.apply(paginatedData.page); - } - }; + public boolean hasNext() { + return paginatedData.fetchNextPage(); + } + + @Override + public T next() { + return pageSupplier.apply(paginatedData.page); } }; } + public CompletableFuture fetchNextPageAsync() { + if (dataClosed) { + return CompletableFuture.completedFuture(false); + } + + for (PaginationStrategy strategy : strategies) { + HttpRequest.Builder requestBuilder = strategy.apply(this); + if (requestBuilder == null) continue; + + ApiCall newApiCall = this.apiCall.toBuilder() + .requestBuilder(requestBuilder) + .build(); + + return newApiCall.executeAsync() + .thenApply(result -> updateWith(newApiCall, result, strategy)) + .exceptionally(ex -> updateAsFailed(ex.getCause())); + } + + return CompletableFuture.completedFuture(false); + } + private boolean fetchNextPage() { - if (caughtFailure) { + if (dataClosed) { return false; } @@ -134,65 +150,48 @@ private boolean fetchNextPage() { if (requestBuilder == null) { continue; } - - boolean isUpdated = tryUpdatingPage(requestBuilder); - if (isUpdated) { - strategy.addMetaData(page); - } - return isUpdated; + try { + ApiCall apiCall = this.apiCall.toBuilder() + .requestBuilder(requestBuilder).build(); + Res pageUnWrapped = apiCall.execute(); + + return updateWith(apiCall, pageUnWrapped, strategy); + } catch (IOException | CoreApiException exp) { + return updateAsFailed(exp); + } } + return false; } - private boolean tryUpdatingPage(HttpRequest.Builder requestBuilder) { - ApiCall apiCall = this.apiCall.toBuilder() - .requestBuilder(requestBuilder).build(); - PageWrapper page; - List> items; - - try { - Res pageUnWrapped = apiCall.execute(); - if (pageUnWrapped == null) { - return false; - } - List itemsUnWrapped = responseToItems.apply(pageUnWrapped); - if (itemsUnWrapped == null) { - return false; - } - page = new PageWrapper<>(apiCall.getResponse().getStatusCode(), - apiCall.getResponse().getHeaders(), pageUnWrapped, itemsUnWrapped); - items = itemsUnWrapped.stream().map(i -> new ItemWrapper() { - @Override - public I get() throws ExceptionType, IOException { - return i; - } - }).collect(Collectors.toList()); - } catch (IOException | CoreApiException exp) { - caughtFailure = true; - page = PageWrapper.CreateError(exp); - items = Arrays.asList(new ItemWrapper() { - @Override - public I get() throws ExceptionType, IOException { - throw exp; - } - }); + private boolean updateWith(ApiCall apiCall, Res pageUnWrapped, PaginationStrategy strategy) { + if (pageUnWrapped == null) { + return false; } - - return updateWith(apiCall, page, items); - } - private boolean updateWith(ApiCall apiCall, - PageWrapper page, List> items) { - if (items.size() == 0) { + List itemsUnWrapped = itemsCreator.apply(pageUnWrapped); + if (itemsUnWrapped == null || itemsUnWrapped.size() == 0) { return false; } - - currentIndex = 0; + + itemIndex = 0; this.apiCall = apiCall; - this.page = page; - this.items = items; - lastPageSize = items.size(); + PageWrapper pageWrapper = PageWrapper.Create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); + strategy.addMetaData(pageWrapper); + this.page = CheckedSupplier.Create(pageCreator.apply(pageWrapper)); + this.items.clear(); + itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.Create(i))); + + return true; + } + + private boolean updateAsFailed(Throwable exp) { + page = CheckedSupplier.CreateError(exp); + itemIndex = 0; + items.clear(); + items.add(CheckedSupplier.CreateError(exp)); + dataClosed = true; return true; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java index baa5ab09..1adcdfda 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java @@ -10,5 +10,5 @@ public interface PaginationStrategy { */ HttpRequest.Builder apply(PaginatedData paginatedData); - void addMetaData(PageWrapper page); + void addMetaData(PageWrapper page); } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index c75f6d0b..9ff92121 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -43,7 +43,7 @@ public void testValidOffsetHeaderReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); @@ -63,7 +63,7 @@ public void testValidOffsetTemplateReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.path#/offset"); @@ -83,7 +83,7 @@ public void testValidOffsetReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value(INITIAL_OFFSET))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -108,7 +108,7 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { put("val", "1"); } }))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); @@ -128,7 +128,7 @@ public void testValidStringOffsetReturnsTrue() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value("5"))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -148,7 +148,7 @@ public void testInvalidStringOffsetReturnsFalse() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value("5a"))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -166,7 +166,7 @@ public void testMissingOffsetReturnsFalse() { PaginatedData paginatedData = mock(PaginatedData.class); when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -186,7 +186,7 @@ public void testNullOffsetReturnsFalse() { when(paginatedData.getLastRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value(NUMERIC_OFFSET))); - when(paginatedData.getLastPageSize()).thenReturn(PAGE_SIZE); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination(null); From 2710816b40172f965681212f6d87c4e329edbd45 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Wed, 4 Jun 2025 16:38:18 +0500 Subject: [PATCH 04/33] update unit tests --- .../core/types/pagination/PageWrapper.java | 5 - .../core/types/pagination/PaginatedData.java | 13 ++- src/test/java/apimatic/core/EndToEndTest.java | 104 ++++++++---------- .../type/pagination/CursorPaginationTest.java | 13 ++- 4 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 09db28ed..c72a0f16 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -1,6 +1,5 @@ package io.apimatic.core.types.pagination; -import java.io.IOException; import java.util.List; import io.apimatic.coreinterfaces.http.HttpHeaders; @@ -98,8 +97,6 @@ public HttpHeaders getHeaders() { /** * Content of the page. * @return Content - * @throws E - * @throws IOException */ public P getResult() { return page; @@ -108,8 +105,6 @@ public P getResult() { /** * Content of the page. * @return List of items on this page. - * @throws E - * @throws IOException */ public List getItems() { return items; diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index dcb2d725..f9594bbe 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -93,6 +94,10 @@ public boolean hasNext() { @Override public T next() { + if (paginatedData.itemIndex == paginatedData.items.size()) { + throw new NoSuchElementException("No more items available."); + } + return itemSupplier.apply(paginatedData.items.get(paginatedData.itemIndex++)); } }; @@ -113,7 +118,13 @@ public boolean hasNext() { @Override public T next() { - return pageSupplier.apply(paginatedData.page); + if (paginatedData.page == null) { + throw new NoSuchElementException("No more pages available."); + } + + T page = pageSupplier.apply(paginatedData.page); + paginatedData.page = null; + return page; } }; } diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index 989bb523..5bf8079b 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -1,6 +1,7 @@ package apimatic.core; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; @@ -24,8 +25,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import com.fasterxml.jackson.core.type.TypeReference; - import apimatic.core.exceptions.GlobalTestException; import apimatic.core.mocks.MockCoreConfig; import apimatic.core.type.pagination.RecordPage; @@ -37,6 +36,7 @@ import io.apimatic.core.types.pagination.LinkPagination; import io.apimatic.core.types.pagination.OffsetPagination; import io.apimatic.core.types.pagination.PagePagination; +import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.core.types.pagination.PaginationStrategy; import io.apimatic.core.utilities.CoreHelper; @@ -171,72 +171,48 @@ public void testEndToEndSyncCall() throws IOException, CoreApiException { @Test public void testLinkPaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new LinkPagination("$response.body#/next_link")).execute()); + verifyData(getPaginatedApiCall(new LinkPagination("$response.body#/next_link"))); } @Test public void testCursorPaginationData() throws IOException, CoreApiException { verifyData(getPaginatedApiCall(new CursorPagination("$response.body#/page_info", - "$request.path#/cursor")).execute()); + "$request.path#/cursor"))); } @Test public void testOffsetPaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new OffsetPagination("$request.headers#/offset")).execute()); + verifyData(getPaginatedApiCall(new OffsetPagination("$request.headers#/offset"))); } @Test public void testPagePaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new PagePagination("$request.query#/page")).execute()); - verifyOnlyPages(getPaginatedApiCall(new PagePagination("$request.query#/page")).execute()); + verifyData(getPaginatedApiCall(new PagePagination("$request.query#/page"))); } - private void verifyOnlyPages(PaginatedData paginatedData) { - RecordPage expectedPage1 = new RecordPage(); - expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); - expectedPage1.setPageInfo("fruits"); - expectedPage1.setNextLink("https://localhost:3000/path?page=2"); - - RecordPage expectedPage2 = new RecordPage(); - expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); - expectedPage2.setPageInfo("vegitables"); - expectedPage2.setNextLink(null); - - int pageNum = 0; - List expectedPages = Arrays.asList(expectedPage1, expectedPage2); - for (RecordPage p : paginatedData.pages()) { - assertEquals(expectedPages.get(pageNum).getData(), p.getData()); - assertEquals(expectedPages.get(pageNum).getPageInfo(), p.getPageInfo()); - assertEquals(expectedPages.get(pageNum).getNextLink(), p.getNextLink()); - pageNum++; - } - - Iterator iterator = paginatedData.pages().iterator(); - Exception exception = assertThrows(NoSuchElementException.class, () -> { - while (iterator.hasNext()) { - iterator.next(); - } - iterator.next(); - }); - assertEquals("No more data available.", exception.getMessage()); - } - - private void verifyData(PaginatedData paginatedData) { + private void verifyData(PaginatedData, RecordPage, CoreApiException> paginatedData) { int index = 0; List expectedData = Arrays.asList( "apple", "mango", "orange", "potato", "carrot", "tomato"); - Iterator iterator = paginatedData.iterator(); - while (iterator.hasNext()) { - String d = iterator.next(); + Iterator itemIterator = paginatedData.items(cs -> { + try { + return cs.get(); + } catch (CoreApiException | IOException e) { + return null; + } + }); + while (itemIterator.hasNext()) { + String d = itemIterator.next(); + assertNotNull(d); assertEquals(expectedData.get(index), d); index++; } Exception exception = assertThrows(NoSuchElementException.class, () -> { - iterator.next(); + itemIterator.next(); }); - assertEquals("No more data available.", exception.getMessage()); + assertEquals("No more items available.", exception.getMessage()); RecordPage expectedPage1 = new RecordPage(); expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); @@ -250,10 +226,19 @@ private void verifyData(PaginatedData paginatedData) { int pageNum = 0; List expectedPages = Arrays.asList(expectedPage1, expectedPage2); - for (RecordPage p : paginatedData.pages()) { - assertEquals(expectedPages.get(pageNum).getData(), p.getData()); - assertEquals(expectedPages.get(pageNum).getPageInfo(), p.getPageInfo()); - assertEquals(expectedPages.get(pageNum).getNextLink(), p.getNextLink()); + Iterator> pagesIterator = paginatedData.pages(cs -> { + try { + return cs.get(); + } catch (CoreApiException | IOException e) { + return null; + } + }); + while (pagesIterator.hasNext()) { + PageWrapper p = pagesIterator.next(); + assertNotNull(p); + assertEquals(expectedPages.get(pageNum).getData(), p.getResult().getData()); + assertEquals(expectedPages.get(pageNum).getPageInfo(), p.getResult().getPageInfo()); + assertEquals(expectedPages.get(pageNum).getNextLink(), p.getResult().getNextLink()); pageNum++; } } @@ -519,22 +504,27 @@ private ApiCall getApiCall() throws IOException { .build(); } - private ApiCall, CoreApiException> getPaginatedApiCall( + private PaginatedData, RecordPage, CoreApiException> getPaginatedApiCall( PaginationStrategy... pagination) throws IOException { - when(response.getBody()).thenReturn("{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\",\"next_link\":\"https://localhost:3000/path?page=2\"}"); when(response.getHeaders()).thenReturn(getHttpHeaders()); Callback pageCallback = new Callback() { private int callNumber = 1; @Override public void onBeforeRequest(Request request) { - if (callNumber > 1) { + if (callNumber == 1) { + when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + } + else if (callNumber == 2) { when(response.getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"}"); } - if (callNumber > 2) { - throw new NoSuchElementException("No more data available."); + else if (callNumber == 3) { + when(response.getBody()).thenReturn( + "{\"data\":[]}"); } callNumber++; } @@ -543,7 +533,7 @@ public void onAfterResponse(Context context) { // TODO Auto-generated method stub } }; - return new ApiCall.Builder, CoreApiException>() + return new ApiCall.Builder() .globalConfig(getGlobalConfig(pageCallback)) .requestBuilder(requestBuilder -> requestBuilder.server("https://localhost:3000") .path("/path/{cursor}") @@ -555,14 +545,14 @@ public void onAfterResponse(Context context) { .headerParam(param -> param.key("accept").value("application/json")) .httpMethod(Method.GET)) .responseHandler(responseHandler -> responseHandler - .paginatedDeserializer(new TypeReference() {}, - t -> t.getData(), r -> r, pagination) + .deserializer(res -> CoreHelper.deserialize(res, RecordPage.class)) .nullify404(false) .globalErrorCase(Collections.emptyMap())) .endpointConfiguration( param -> param.arraySerializationFormat(ArraySerializationFormat.INDEXED) .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) - .build(); + .build() + .paginate(p -> p, pw -> pw, r -> r.getData(), pagination); } private ApiCall getApiCallLocalErrorTemplate(String responseString, diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 6516c80f..f5a36983 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -14,6 +14,7 @@ import org.mockito.junit.MockitoRule; import io.apimatic.core.HttpRequest; +import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.CursorPagination; import io.apimatic.core.types.pagination.PaginatedData; @@ -36,20 +37,20 @@ public class CursorPaginationTest { */ @Test public void testWithValidCursorReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse().getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); - assertTrue(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); - cursor.getNextRequestBuilder().updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference("$request.query#/cursor", v -> { assertEquals("xyz123", v); return v; }); From 8b900775fc9f51d62eb8e20254e0bfc5cc3f4e28 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 5 Jun 2025 11:49:46 +0500 Subject: [PATCH 05/33] test(pagination): add test for pagination classes --- .../type/pagination/CursorPaginationTest.java | 141 ++++++------- .../type/pagination/LinkPaginationTest.java | 160 +++++++------- .../type/pagination/OffsetPaginationTest.java | 198 +++++++++++------- .../type/pagination/PagePaginationTest.java | 125 ++++++----- 4 files changed, 347 insertions(+), 277 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index f5a36983..4aba429c 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -17,6 +18,7 @@ import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.CursorPagination; import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.coreinterfaces.http.response.Response; /** * Unit tests for the {@link CursorPagination} class. @@ -38,11 +40,13 @@ public class CursorPaginationTest { @Test public void testWithValidCursorReturnsTrue() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getResponse().getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); @@ -61,181 +65,178 @@ public void testWithValidCursorReturnsTrue() { */ @Test public void testWithValidCursorAndDifferentTypeReturnsTrueA() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": 123}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); - assertTrue(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); - cursor.getNextRequestBuilder().updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference("$request.query#/cursor", v -> { assertEquals("123", v); return v; }); } + @Test public void testWithValidCursorAndDifferentTypeReturnsTrueB() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); final int current = 456; + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("cursor").value(current))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": 123}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); - assertTrue(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); - cursor.getNextRequestBuilder().updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference("$request.query#/cursor", v -> { assertEquals("123", v); return v; }); } + @Test public void testWithValidCursorButMissingInFirstRequestReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); - assertFalse(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); - - cursor.getNextRequestBuilder().updateByReference("$request.query#/cursor", v -> { - fail(); - return v; - }); + Builder requestBuilder = cursor.apply(paginatedData); + assertNull(requestBuilder); } + /** * Test with valid cursor from a response body and different type. */ @Test public void testWithValidCursorFromResponseBodyReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": 123}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.query#/cursor"); - assertTrue(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); - cursor.getNextRequestBuilder().updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference("$request.query#/cursor", v -> { assertEquals("123", v); return v; }); } + /** * Test case where the response pointer is invalid. */ @Test public void testWithInvalidResponsePointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); - CursorPagination cursor = new CursorPagination("$response.body#/next", + CursorPagination cursor = new CursorPagination("$response.body#/next", // invalid pointer "$request.headers#/cursor"); - assertFalse(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); - - cursor.getNextRequestBuilder().updateByReference("$request.headers#/cursor", v -> { - assertEquals("abc", v); - return v; - }); + Builder requestBuilder = cursor.apply(paginatedData); + assertNull(requestBuilder); } + /** * Test with missing response pointer. */ @Test public void testWithMissingResponsePointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination(null, "$request.headers#/cursor"); - assertFalse(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); - - cursor.getNextRequestBuilder().updateByReference("$request.headers#/cursor", v -> { - assertEquals("abc", v); - return v; - }); + Builder requestBuilder = cursor.apply(paginatedData); + assertNull(requestBuilder); } + /** * Test case with invalid request pointer. */ @Test public void testWithInvalidRequestPointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", "$request.headers#/next_cursor"); - assertFalse(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); - - cursor.getNextRequestBuilder().updateByReference("$request.headers#/cursor", v -> { - assertEquals("abc", v); - return v; - }); + assertNull(cursor.apply(paginatedData)); } + /** * Test with missing request pointer. */ @Test public void testWithMissingRequestPointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); - when(paginatedData.getLastResponseBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", null); + assertNull(cursor.apply(paginatedData)); + } - assertFalse(cursor.isValid(paginatedData)); - assertNotNull(cursor.getNextRequestBuilder()); - cursor.getNextRequestBuilder().updateByReference("$request.headers#/cursor", v -> { - assertEquals("abc", v); - return v; - }); - } } diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 3f9ebf2d..114c1731 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -3,19 +3,29 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.junit.Rule; import org.junit.Test; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import io.apimatic.core.HttpRequest; +import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.LinkPagination; import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.coreinterfaces.http.HttpHeaders; +import io.apimatic.coreinterfaces.http.response.Response; +import okhttp3.Headers; /** * Unit tests for the LinkPagination class. @@ -30,18 +40,19 @@ public class LinkPaginationTest { @Test public void testValidLinkReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()) - .thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); LinkPagination link = new LinkPagination("$response.body#/next"); - assertTrue(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); + Builder requestBuilder = link.apply(paginatedData); + assertNotNull(requestBuilder); - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference("$request.query#/page", v -> { assertEquals("2", v); return v; }); @@ -49,33 +60,34 @@ public void testValidLinkReturnsTrue() { @Test public void testValidLinkWithAdditionalParamsReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final int pageSize = 456; - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder() + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder() .queryParam(q -> q.key("size").value(pageSize)) .queryParam(q -> q.key("page").value(1)) .headerParam(h -> h.key("page").value(2))); - when(paginatedData.getLastResponseBody()) - .thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); LinkPagination link = new LinkPagination("$response.body#/next"); - assertTrue(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); + Builder requestBuilder = link.apply(paginatedData); + assertNotNull(requestBuilder); - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference("$request.query#/page", v -> { assertEquals("2", v); return v; }); - link.getNextRequestBuilder().updateByReference("$request.query#/size", v -> { + requestBuilder.updateByReference("$request.query#/size", v -> { assertEquals(pageSize, v); return v; }); - link.getNextRequestBuilder().updateByReference("$request.headers#/page", v -> { + requestBuilder.updateByReference("$request.headers#/page", v -> { assertEquals(2, v); return v; }); @@ -83,125 +95,127 @@ public void testValidLinkWithAdditionalParamsReturnsTrue() { @Test public void testValidLinkFromHeaderReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + Map> headers = new HashMap<>(); + headers.put("next", Arrays.asList("https://api.example.com?page=2")); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseHeaders()) - .thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getHeaders()).thenReturn((HttpHeaders) headers); + // Test the link pagination LinkPagination link = new LinkPagination("$response.headers#/next"); + Builder requestBuilder = link.apply(paginatedData); - assertTrue(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); - - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - assertEquals("2", v); + // Verify results + assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/page", v -> { + assertEquals("2", v); // Page extracted from URL query param return v; }); } + @Test public void testInvalidPointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()) - .thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); LinkPagination link = new LinkPagination("$response.body#/next/href"); - assertFalse(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); - - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - fail(); - return v; - }); + // Since pointer is invalid, apply(...) should return null + assertNull(link.apply(paginatedData)); } + @Test public void testMissingResponseReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()).thenReturn(null); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn(null); LinkPagination link = new LinkPagination("$response.body#/next/href"); - assertFalse(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); - - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - fail(); - return v; - }); + // Since response body is null, apply should return null + assertNull(link.apply(paginatedData)); } + @Test public void testMissingPointerReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()) - .thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); LinkPagination link = new LinkPagination(null); - assertFalse(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); - - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - fail(); - return v; - }); + // Pointer is null, apply should return null + assertNull(link.apply(paginatedData)); } @Test public void testMultipleQueryParamsReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()) + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()) .thenReturn("{\"next\": \"https://api.example.com?page=2&size=5\"}"); LinkPagination link = new LinkPagination("$response.body#/next"); - assertTrue(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); + Builder nextBuilder = link.apply(paginatedData); + assertNotNull(nextBuilder); - link.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + nextBuilder.updateByReference("$request.query#/page", v -> { assertEquals("2", v); return v; }); - link.getNextRequestBuilder().updateByReference("$request.query#/size", v -> { + nextBuilder.updateByReference("$request.query#/size", v -> { assertEquals("5", v); return v; }); } + @Test public void testEncodedQueryParamsReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); - when(paginatedData.getLastResponseBody()) - .thenReturn("{\"next\": \"https://api.example.com?page%20o=2%20a&" - + "size%20q=5^%214$#\"}"); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()) + .thenReturn("{\"next\": \"https://api.example.com?page%20o=2%20a&size%20q=5^%214$#\"}"); LinkPagination link = new LinkPagination("$response.body#/next"); - assertTrue(link.isValid(paginatedData)); - assertNotNull(link.getNextRequestBuilder()); + Builder builder = link.apply(paginatedData); + assertNotNull(builder); - link.getNextRequestBuilder().updateByReference("$request.query#/page o", v -> { + builder.updateByReference("$request.query#/page o", v -> { assertEquals("2 a", v); return v; }); - link.getNextRequestBuilder().updateByReference("$request.query#/size q", v -> { + builder.updateByReference("$request.query#/size q", v -> { assertEquals("5^!4$#", v); return v; }); } + } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 9ff92121..72d90aef 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -9,6 +9,8 @@ import static org.mockito.Mockito.when; import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Rule; import org.junit.Test; @@ -16,6 +18,7 @@ import org.mockito.junit.MockitoRule; import io.apimatic.core.HttpRequest; +import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.OffsetPagination; import io.apimatic.core.types.pagination.PaginatedData; @@ -38,19 +41,22 @@ public class OffsetPaginationTest { @Test public void testValidOffsetHeaderReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .headerParam(h -> h.key("offset").value(INITIAL_OFFSET)); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key("offset").value(INITIAL_OFFSET))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); - - assertTrue(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.headers#/offset", v -> { + Builder nextRequestBuilder = offset.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + nextRequestBuilder.updateByReference("$request.headers#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -58,19 +64,22 @@ public void testValidOffsetHeaderReturnsTrue() { @Test public void testValidOffsetTemplateReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .templateParam(t -> t.key("offset").value(INITIAL_OFFSET)); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().templateParam( - t -> t.key("offset").value(INITIAL_OFFSET))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.path#/offset"); - - assertTrue(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.path#/offset", v -> { + Builder nextRequestBuilder = offset.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + nextRequestBuilder.updateByReference("$request.path#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -78,19 +87,22 @@ public void testValidOffsetTemplateReturnsTrue() { @Test public void testValidOffsetReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .queryParam(q -> q.key("offset").value(INITIAL_OFFSET)); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value(INITIAL_OFFSET))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - assertTrue(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset", v -> { + Builder nextRequestBuilder = offset.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -98,24 +110,26 @@ public void testValidOffsetReturnsTrue() { @Test public void testValidOffsetAsInnerFieldReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); - - when(paginatedData.getLastRequestBuilder()).thenReturn( - new HttpRequest.Builder().queryParam(q -> q.key("offset").value( - new HashMap() { - private static final long serialVersionUID = 1L; - { - put("val", "1"); - } - }))); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + Map offsetMap = new HashMap() {{ + put("val", "1"); + }}; + + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .queryParam(q -> q.key("offset").value(offsetMap)); + + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination with nested field OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); - - assertTrue(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset/val", v -> { + Builder nextRequestBuilder = offset.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + nextRequestBuilder.updateByReference("$request.query#/offset/val", v -> { assertEquals(OFFSET_VAL_PLUS_ONE, v); return v; }); @@ -123,79 +137,103 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { @Test public void testValidStringOffsetReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .queryParam(q -> q.key("offset").value("5")); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value("5"))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination with string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - assertTrue(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset", v -> { + Builder nextRequestBuilder = offset.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { assertEquals(OFFSET_STRING_PLUS_PAGE, v); return v; }); } - @Test public void testInvalidStringOffsetReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .queryParam(q -> q.key("offset").value("5a")); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value("5a"))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination with invalid string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - assertFalse(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset", v -> { - assertEquals("5a", v); + + // Verify the offset remains unchanged for invalid values + Builder nextRequestBuilder = offset.apply(paginatedData); + assertNotNull(nextRequestBuilder); + + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals("5a", v); // Value should remain unchanged return v; }); } @Test public void testMissingOffsetReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder(); // No offset param - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test the offset pagination with missing offset OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - assertFalse(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset", v -> { - fail(); + + // Execute and verify + Builder nextRequestBuilder = offset.apply(paginatedData); + assertNotNull("Builder should still be returned even with missing offset", nextRequestBuilder); + + // Verify no update is attempted for missing offset + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { + callbackInvoked.set(true); return v; }); + + assertFalse("Callback should not be invoked for missing offset", callbackInvoked.get()); } @Test public void testNullOffsetReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .queryParam(q -> q.key("offset").value(NUMERIC_OFFSET)); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value(NUMERIC_OFFSET))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Test with null offset configuration OffsetPagination offset = new OffsetPagination(null); - - assertFalse(offset.isValid(paginatedData)); - assertNotNull(offset.getNextRequestBuilder()); - - offset.getNextRequestBuilder().updateByReference("$request.query#/offset", v -> { - assertEquals(NUMERIC_OFFSET, v); + + // Execute and verify + Builder nextRequestBuilder = offset.apply(paginatedData); + assertNotNull("Builder should still be returned even with null offset config", nextRequestBuilder); + + // Verify original offset remains unchanged + AtomicBoolean callbackInvoked = new AtomicBoolean(false); + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { + callbackInvoked.set(true); + assertEquals("Original offset should remain unchanged", NUMERIC_OFFSET, v); return v; }); + + assertTrue("Should still process existing offset values", callbackInvoked.get()); } } diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 662133a6..d1687eaa 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -9,6 +9,8 @@ import static org.mockito.Mockito.when; import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Rule; import org.junit.Test; @@ -16,6 +18,7 @@ import org.mockito.junit.MockitoRule; import io.apimatic.core.HttpRequest; +import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.PagePagination; import io.apimatic.core.types.pagination.PaginatedData; @@ -32,164 +35,178 @@ public class PagePaginationTest { @Test public void testWithValidPageHeaderReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + // Setup mocks + PaginatedData paginatedData = mock(PaginatedData.class); final int initialPage = 3; final int nextPage = 4; + + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() + .headerParam(h -> h.key("page").value(initialPage)); - when(paginatedData.getLastRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key("page").value(initialPage))); + // Mock behaviors + when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + // Test the page pagination PagePagination page = new PagePagination("$request.headers#/page"); - - assertTrue(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); - - page.getNextRequestBuilder().updateByReference("$request.headers#/page", v -> { - assertEquals(nextPage, v); + Builder nextRequestBuilder = page.apply(paginatedData); + + // Verify results + assertNotNull(nextRequestBuilder); + + // Verify page increment + AtomicReference pageValue = new AtomicReference<>(); + nextRequestBuilder.updateByReference("$request.headers#/page", v -> { + pageValue.set(v); return v; }); + + assertEquals(nextPage, pageValue.get()); } + @Test public void testWithValidPageTemplateReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); final int initialPage = 3; final int nextPage = 4; - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("page").value(initialPage))); PagePagination page = new PagePagination("$request.path#/page"); - assertTrue(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.path#/page", v -> { + requestBuilder.updateByReference("$request.path#/page", v -> { assertEquals(nextPage, v); return v; }); } + @Test public void testWithValidPageReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); final int initialPage = 3; final int nextPage = 4; - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(initialPage))); PagePagination page = new PagePagination("$request.query#/page"); - assertTrue(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference("$request.query#/page", v -> { assertEquals(nextPage, v); return v; }); } + @Test public void testWithValidPageAsInnerFieldReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); - when(paginatedData.getLastRequestBuilder()).thenReturn( - new HttpRequest.Builder().queryParam(q -> q.key("page") - .value(new HashMap() { - private static final long serialVersionUID = 1L; - { - put("val", "1"); - } - }))); + Map pageValue = new HashMap<>(); + pageValue.put("val", "1"); + + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key("page").value(pageValue))); PagePagination page = new PagePagination("$request.query#/page/val"); - assertTrue(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page/val", v -> { + requestBuilder.updateByReference("$request.query#/page/val", v -> { assertEquals(2, v); return v; }); } + @Test public void testWithValidStringPageReturnsTrue() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); final String current = "5"; final int next = 6; - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(current))); PagePagination page = new PagePagination("$request.query#/page"); - assertTrue(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference("$request.query#/page", v -> { assertEquals(next, v); return v; }); } + @Test public void testWithInvalidStringPageReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value("5a"))); PagePagination page = new PagePagination("$request.query#/page"); - assertFalse(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference("$request.query#/page", v -> { assertEquals("5a", v); return v; }); } + @Test public void testWithMissingPageReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); - when(paginatedData.getLastRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); PagePagination page = new PagePagination("$request.query#/page"); - assertFalse(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - fail(); + requestBuilder.updateByReference("$request.query#/page", v -> { + fail("Should not be called because page is missing"); return v; }); } + @Test public void testWithNullPageReturnsFalse() { - PaginatedData paginatedData = mock(PaginatedData.class); + PaginatedData paginatedData = mock(PaginatedData.class); final int current = 5; - when(paginatedData.getLastRequestBuilder()) + when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(current))); PagePagination page = new PagePagination(null); - assertFalse(page.isValid(paginatedData)); - assertNotNull(page.getNextRequestBuilder()); + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); - page.getNextRequestBuilder().updateByReference("$request.query#/page", v -> { - assertEquals(current, v); + requestBuilder.updateByReference("$request.query#/page", v -> { + assertEquals(current, v); // No update expected; should return current value return v; }); } + } From 9ea0e4f7a948d9739b5a5c9447a8d3308ef3d7e2 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 5 Jun 2025 12:36:26 +0500 Subject: [PATCH 06/33] test(pagination): updated link pagination test --- .../type/pagination/LinkPaginationTest.java | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 114c1731..9d5d1563 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.Rule; import org.junit.Test; @@ -98,12 +99,12 @@ public void testValidLinkFromHeaderReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - Map> headers = new HashMap<>(); - headers.put("next", Arrays.asList("https://api.example.com?page=2")); + Map headers = new HashMap<>(); + headers.put("next", "https://api.example.com?page=2"); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getHeaders()).thenReturn((HttpHeaders) headers); + when(response.getHeaders()).thenReturn(createHttpHeaders(headers)); // Test the link pagination LinkPagination link = new LinkPagination("$response.headers#/next"); @@ -117,6 +118,83 @@ public void testValidLinkFromHeaderReturnsTrue() { }); } + private HttpHeaders createHttpHeaders(Map headers) { + return new HttpHeaders() { + + @Override + public List values(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String value(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List remove(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set names() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean has(String headerName) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map asSimpleMap() { + // TODO Auto-generated method stub + return headers; + } + + @Override + public Map> asMultimap() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addAllFromMultiMap(Map> headers) { + // TODO Auto-generated method stub + + } + + @Override + public void addAllFromMap(Map headers) { + // TODO Auto-generated method stub + + } + + @Override + public void addAll(HttpHeaders headers) { + // TODO Auto-generated method stub + + } + + @Override + public void add(String headerName, List values) { + // TODO Auto-generated method stub + + } + + @Override + public void add(String headerName, String value) { + // TODO Auto-generated method stub + + } + }; + } + @Test public void testInvalidPointerReturnsFalse() { From 228ea05d20bcad41cde9af8d75f14b8150ce0d88 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 5 Jun 2025 15:52:05 +0500 Subject: [PATCH 07/33] test(pagination): updated offset and paged pagination test --- .../type/pagination/OffsetPaginationTest.java | 201 +++++++----------- .../type/pagination/PagePaginationTest.java | 100 ++++----- 2 files changed, 128 insertions(+), 173 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 72d90aef..e6d0e2d2 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -1,16 +1,12 @@ package apimatic.core.type.pagination; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Rule; import org.junit.Test; @@ -21,6 +17,7 @@ import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.OffsetPagination; import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.coreinterfaces.http.response.Response; /** * Unit tests for {@link OffsetPagination}. @@ -41,22 +38,22 @@ public class OffsetPaginationTest { @Test public void testValidOffsetHeaderReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .headerParam(h -> h.key("offset").value(INITIAL_OFFSET)); + Response response = mock(Response.class); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().headerParam( + h -> h.key("offset").value(INITIAL_OFFSET))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); - Builder nextRequestBuilder = offset.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - nextRequestBuilder.updateByReference("$request.headers#/offset", v -> { + + Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.headers#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -64,22 +61,22 @@ public void testValidOffsetHeaderReturnsTrue() { @Test public void testValidOffsetTemplateReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .templateParam(t -> t.key("offset").value(INITIAL_OFFSET)); + Response response = mock(Response.class); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().templateParam( + t -> t.key("offset").value(INITIAL_OFFSET))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.path#/offset"); - Builder nextRequestBuilder = offset.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - nextRequestBuilder.updateByReference("$request.path#/offset", v -> { + + Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.path#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -87,22 +84,22 @@ public void testValidOffsetTemplateReturnsTrue() { @Test public void testValidOffsetReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(INITIAL_OFFSET)); + Response response = mock(Response.class); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().queryParam( + q -> q.key("offset").value(INITIAL_OFFSET))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - Builder nextRequestBuilder = offset.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - nextRequestBuilder.updateByReference("$request.query#/offset", v -> { + + Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.query#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -110,26 +107,27 @@ public void testValidOffsetReturnsTrue() { @Test public void testValidOffsetAsInnerFieldReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - Map offsetMap = new HashMap() {{ - put("val", "1"); - }}; - - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(offsetMap)); - - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + Response response = mock(Response.class); + + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key("offset").value( + new HashMap() { + private static final long serialVersionUID = 1L; + { + put("val", "1"); + } + }))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": {\"val\": 1}}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination with nested field OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); - Builder nextRequestBuilder = offset.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - nextRequestBuilder.updateByReference("$request.query#/offset/val", v -> { + + Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.query#/offset/val", v -> { assertEquals(OFFSET_VAL_PLUS_ONE, v); return v; }); @@ -137,103 +135,68 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { @Test public void testValidStringOffsetReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value("5")); + Response response = mock(Response.class); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().queryParam( + q -> q.key("offset").value("5"))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": \"5\"}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination with string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - Builder nextRequestBuilder = offset.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - nextRequestBuilder.updateByReference("$request.query#/offset", v -> { + + Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.query#/offset", v -> { assertEquals(OFFSET_STRING_PLUS_PAGE, v); return v; }); } + + @Test public void testInvalidStringOffsetReturnsFalse() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value("5a")); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().queryParam( + q -> q.key("offset").value("5a"))); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination with invalid string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - // Verify the offset remains unchanged for invalid values - Builder nextRequestBuilder = offset.apply(paginatedData); - assertNotNull(nextRequestBuilder); - - nextRequestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals("5a", v); // Value should remain unchanged - return v; - }); + + Builder requestBuilder = offset.apply(paginatedData); + assertNull(requestBuilder); } @Test public void testMissingOffsetReturnsFalse() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder(); // No offset param - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test the offset pagination with missing offset OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - - // Execute and verify - Builder nextRequestBuilder = offset.apply(paginatedData); - assertNotNull("Builder should still be returned even with missing offset", nextRequestBuilder); - - // Verify no update is attempted for missing offset - AtomicBoolean callbackInvoked = new AtomicBoolean(false); - nextRequestBuilder.updateByReference("$request.query#/offset", v -> { - callbackInvoked.set(true); - return v; - }); - - assertFalse("Callback should not be invoked for missing offset", callbackInvoked.get()); + + Builder requestBuilder = offset.apply(paginatedData); + assertNull(requestBuilder); } @Test public void testNullOffsetReturnsFalse() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(NUMERIC_OFFSET)); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().queryParam( + q -> q.key("offset").value(NUMERIC_OFFSET))); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Test with null offset configuration OffsetPagination offset = new OffsetPagination(null); - - // Execute and verify - Builder nextRequestBuilder = offset.apply(paginatedData); - assertNotNull("Builder should still be returned even with null offset config", nextRequestBuilder); - - // Verify original offset remains unchanged - AtomicBoolean callbackInvoked = new AtomicBoolean(false); - nextRequestBuilder.updateByReference("$request.query#/offset", v -> { - callbackInvoked.set(true); - assertEquals("Original offset should remain unchanged", NUMERIC_OFFSET, v); - return v; - }); - - assertTrue("Should still process existing offset values", callbackInvoked.get()); + + Builder requestBuilder = offset.apply(paginatedData); + assertNull(requestBuilder); } -} +} \ No newline at end of file diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index d1687eaa..b41c7bba 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -1,16 +1,12 @@ package apimatic.core.type.pagination; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import org.junit.Rule; import org.junit.Test; @@ -21,6 +17,7 @@ import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.PagePagination; import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.coreinterfaces.http.response.Response; /** * Unit tests for PagePagination. @@ -35,44 +32,40 @@ public class PagePaginationTest { @Test public void testWithValidPageHeaderReturnsTrue() { - // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final int initialPage = 3; final int nextPage = 4; - - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .headerParam(h -> h.key("page").value(initialPage)); - // Mock behaviors - when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); + when(paginatedData.getRequestBuilder()) + .thenReturn(new HttpRequest.Builder().headerParam( + h -> h.key("page").value(initialPage))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); - // Test the page pagination PagePagination page = new PagePagination("$request.headers#/page"); - Builder nextRequestBuilder = page.apply(paginatedData); - - // Verify results - assertNotNull(nextRequestBuilder); - - // Verify page increment - AtomicReference pageValue = new AtomicReference<>(); - nextRequestBuilder.updateByReference("$request.headers#/page", v -> { - pageValue.set(v); + + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.headers#/page", v -> { + assertEquals(nextPage, v); return v; }); - - assertEquals(nextPage, pageValue.get()); } - @Test public void testWithValidPageTemplateReturnsTrue() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final int initialPage = 3; final int nextPage = 4; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("page").value(initialPage))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); PagePagination page = new PagePagination("$request.path#/page"); @@ -85,16 +78,18 @@ public void testWithValidPageTemplateReturnsTrue() { }); } - @Test public void testWithValidPageReturnsTrue() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final int initialPage = 3; final int nextPage = 4; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(initialPage))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); PagePagination page = new PagePagination("$request.query#/page"); @@ -107,16 +102,21 @@ public void testWithValidPageReturnsTrue() { }); } - @Test public void testWithValidPageAsInnerFieldReturnsTrue() { PaginatedData paginatedData = mock(PaginatedData.class); - - Map pageValue = new HashMap<>(); - pageValue.put("val", "1"); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn( - new HttpRequest.Builder().queryParam(q -> q.key("page").value(pageValue))); + new HttpRequest.Builder().queryParam(q -> q.key("page") + .value(new HashMap() { + private static final long serialVersionUID = 1L; + { + put("val", "1"); + } + }))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": {\"val\": 1}}"); PagePagination page = new PagePagination("$request.query#/page/val"); @@ -129,16 +129,18 @@ public void testWithValidPageAsInnerFieldReturnsTrue() { }); } - @Test public void testWithValidStringPageReturnsTrue() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final String current = "5"; final int next = 6; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(current))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": \"" + current + "\"}"); PagePagination page = new PagePagination("$request.query#/page"); @@ -151,62 +153,52 @@ public void testWithValidStringPageReturnsTrue() { }); } - @Test public void testWithInvalidStringPageReturnsFalse() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value("5a"))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": \"5a\"}"); PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); - assertNotNull(requestBuilder); - - requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals("5a", v); - return v; - }); + assertNull(requestBuilder); } - @Test public void testWithMissingPageReturnsFalse() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{}"); PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); - assertNotNull(requestBuilder); - - requestBuilder.updateByReference("$request.query#/page", v -> { - fail("Should not be called because page is missing"); - return v; - }); + assertNull(requestBuilder); } - @Test public void testWithNullPageReturnsFalse() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); final int current = 5; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(current))); + when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + current + "}"); PagePagination page = new PagePagination(null); Builder requestBuilder = page.apply(paginatedData); - assertNotNull(requestBuilder); - - requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals(current, v); // No update expected; should return current value - return v; - }); + assertNull(requestBuilder); } - -} +} \ No newline at end of file From e087ae07ef893262a0a492ffbef551b87466a4f8 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 5 Jun 2025 15:59:18 +0500 Subject: [PATCH 08/33] adds end to end tests --- .../java/io/apimatic/core/HttpRequest.java | 81 ++-- .../types/pagination/CursorPagination.java | 4 + .../types/pagination/OffsetPagination.java | 4 + .../core/types/pagination/PagePagination.java | 4 + .../core/types/pagination/PaginatedData.java | 10 +- src/test/java/apimatic/core/EndToEndTest.java | 374 ++++++++++++++++-- .../type/pagination/CursorPaginationTest.java | 3 - .../type/pagination/LinkPaginationTest.java | 5 - .../type/pagination/OffsetPaginationTest.java | 57 +-- .../type/pagination/PagePaginationTest.java | 2 - 10 files changed, 435 insertions(+), 109 deletions(-) diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 6d3891f1..8e0f0ad1 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -66,9 +66,9 @@ public final class HttpRequest { * @param queryParams * @param templateParams * @param headerParams - * @param formParams - * @param body + * @param multipartFormParams * @param formParameters + * @param body * @param bodySerializer * @param bodyParameters * @param arraySerializationFormat @@ -78,7 +78,7 @@ private HttpRequest(final GlobalConfiguration coreConfig, final String server, final String path, final Method httpMethod, final Authentication authentication, final Map queryParams, final Map> templateParams, - final Map> headerParams, final Set formParams, + final Map> headerParams, final Set multipartFormParams, final Map formParameters, final Object body, final Serializer bodySerializer, final Map bodyParameters, final ArraySerializationFormat arraySerializationFormat) throws IOException { @@ -100,7 +100,7 @@ private HttpRequest(final GlobalConfiguration coreConfig, final String server, processTemplateParams(templateParams); Object bodyValue = buildBody(body, bodySerializer, bodyParameters); List> formFields = - generateFormFields(formParams, formParameters, arraySerializationFormat); + generateFormFields(multipartFormParams, formParameters, arraySerializationFormat); coreHttpRequest = buildRequest(httpMethod, bodyValue, requestHeaders, queryParams, formFields, arraySerializationFormat); @@ -150,31 +150,27 @@ private void applyAuthentication(Request request, Authentication authentication) } /** - * @param formParams - * @param optionalFormParamaters + * @param multipartFormParams + * @param formParameters * @param arraySerializationFormat * @return list of form parameters * @throws IOException */ private List> generateFormFields( - Set formParams, Map optionalFormParamaters, + Set multipartFormParams, Map formParameters, ArraySerializationFormat arraySerializationFormat) throws IOException { - if (formParams.isEmpty() && optionalFormParamaters.isEmpty()) { + if (multipartFormParams.isEmpty() && formParameters.isEmpty()) { return null; } - Map formParameters = new HashMap<>(); - for (Parameter formParameter : formParams) { - String key = formParameter.getKey(); - Object value = formParameter.getValue(); - if (formParameter.getMultiPartRequest() != null) { - value = handleMultiPartRequest(formParameter); - } + Map formParamsMap = new HashMap<>(); - formParameters.put(key, value); + for (Parameter formParameter : multipartFormParams) { + Object value = handleMultiPartRequest(formParameter); + formParamsMap.put(formParameter.getKey(), value); } - formParameters.putAll(optionalFormParamaters); - return CoreHelper.prepareFormFields(formParameters, arraySerializationFormat); + formParamsMap.putAll(formParameters); + return CoreHelper.prepareFormFields(formParamsMap, arraySerializationFormat); } private StringBuilder getStringBuilder(String server, String path, @@ -295,7 +291,7 @@ public static class Builder { /** * A set of {@link Parameter}. */ - private Set formParams = new HashSet<>(); + private Set multipartFormParams = new HashSet<>(); /** * A map of form parameters @@ -354,11 +350,41 @@ public Builder updateByReference(String pointer, UnaryOperator setter) { case "$request.headers": updateHeaderParams(setter, point); return this; + case "$request.body": + updateBodyParams(setter, point); + return this; default: return this; } } + private void updateBodyParams(UnaryOperator setter, String point) { + if (body != null) { + if (body instanceof CoreFileWrapper) { + return; + } + + if (bodySerializer != null && point == "") { + bodySerializer = () -> setter.apply(bodySerializer.supply()).toString(); + return; + } + + if ((body instanceof String && point == "") || point == "") { + body = setter.apply(body); + return; + } + + body = CoreHelper.updateValueByPointer(body, point, setter); + } + + if (bodyParameters != null) { + bodyParameters = CoreHelper.updateValueByPointer(bodyParameters, point, setter); + return; + } + + formParamaters = CoreHelper.updateValueByPointer(formParamaters, point, setter); + } + @SuppressWarnings("unchecked") private void updateHeaderParams(UnaryOperator setter, String point) { Map simplifiedHeaders = new HashMap<>(); @@ -391,10 +417,8 @@ private void updateTemplateParams(UnaryOperator setter, String point) { simplifiedPath = CoreHelper.updateValueByPointer(simplifiedPath, point, setter); for (Map.Entry entry : simplifiedPath.entrySet()) { - // Preserve the original boolean if it exists, otherwise set default (e.g., false) - Boolean originalFlag = templateParams.containsKey(entry.getKey()) - ? templateParams.get(entry.getKey()).getValue() - : false; + // Preserve the original boolean encoding flag + Boolean originalFlag = templateParams.get(entry.getKey()).getValue(); templateParams.put(entry.getKey(), new SimpleEntry<>(entry.getValue(), originalFlag)); } @@ -512,7 +536,12 @@ public Builder formParam(Consumer action) { action.accept(parameterBuilder); Parameter formParameter = parameterBuilder.build(); formParameter.validate(); - this.formParams.add(formParameter); + if (formParameter.getMultiPartRequest() != null) { + this.multipartFormParams.add(formParameter); + return this; + } + this.formParamaters.put(formParameter.getKey(), formParameter.getValue()); + return this; } @@ -623,7 +652,7 @@ public Builder copy() { for (Map.Entry> entry : this.headerParams.entrySet()) { copy.headerParams.put(entry.getKey(), new ArrayList<>(entry.getValue())); } - copy.formParams = new HashSet<>(this.formParams); + copy.multipartFormParams = new HashSet<>(this.multipartFormParams); copy.formParamaters = new HashMap<>(this.formParamaters); copy.body = this.body; copy.bodySerializer = this.bodySerializer; @@ -646,7 +675,7 @@ public Request build(GlobalConfiguration coreConfig) throws IOException { Authentication authentication = authBuilder.build(coreConfig.getAuthentications()); HttpRequest coreRequest = new HttpRequest(coreConfig, server, path, httpMethod, authentication, - queryParams, templateParams, getHeaderParams(), formParams, + queryParams, templateParams, getHeaderParams(), multipartFormParams, formParamaters, body, bodySerializer, bodyParameters, arraySerializationFormat); Request coreHttpRequest = coreRequest.getCoreHttpRequest(); diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index fd40b25d..3872d457 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -42,6 +42,10 @@ public Builder apply(PaginatedData paginatedData) { isUpdated[0] = true; return cursorValue; }); + + if (!isUpdated[0] && response == null) { + return reqBuilder; + } return isUpdated[0] ? reqBuilder : null; } diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index 273fb955..2bee47f4 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -34,6 +34,10 @@ public Builder apply(PaginatedData paginatedData) { isUpdated[0] = true; return newValue; }); + + if (!isUpdated[0] && response == null) { + return reqBuilder; + } return isUpdated[0] ? reqBuilder : null; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index 4efd0f26..c9f07ad8 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -34,6 +34,10 @@ public Builder apply(PaginatedData paginatedData) { isUpdated[0] = true; return newValue; }); + + if (!isUpdated[0] && response == null) { + return reqBuilder; + } return isUpdated[0] ? reqBuilder : null; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index f9594bbe..f853672d 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -113,6 +113,10 @@ public Iterator pages(Function, T> page return new Iterator() { @Override public boolean hasNext() { + if (paginatedData.page != null) { + return true; + } + return paginatedData.fetchNextPage(); } @@ -177,6 +181,10 @@ private boolean fetchNextPage() { } private boolean updateWith(ApiCall apiCall, Res pageUnWrapped, PaginationStrategy strategy) { + itemIndex = 0; + this.items.clear(); + this.page = null; + if (pageUnWrapped == null) { return false; } @@ -186,12 +194,10 @@ private boolean updateWith(ApiCall apiCall, Res pageUnWrappe return false; } - itemIndex = 0; this.apiCall = apiCall; PageWrapper pageWrapper = PageWrapper.Create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); strategy.addMetaData(pageWrapper); this.page = CheckedSupplier.Create(pageCreator.apply(pageWrapper)); - this.items.clear(); itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.Create(i))); return true; diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index 5bf8079b..430f157f 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -1,8 +1,11 @@ package apimatic.core; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyMap; @@ -17,6 +20,9 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; import org.junit.Before; import org.junit.Rule; @@ -32,6 +38,7 @@ import io.apimatic.core.ErrorCase; import io.apimatic.core.GlobalConfiguration; import io.apimatic.core.types.CoreApiException; +import io.apimatic.core.types.pagination.CheckedSupplier; import io.apimatic.core.types.pagination.CursorPagination; import io.apimatic.core.types.pagination.LinkPagination; import io.apimatic.core.types.pagination.OffsetPagination; @@ -169,49 +176,307 @@ public void testEndToEndSyncCall() throws IOException, CoreApiException { assertEquals(actual, expected); } + @Test + public void testInvalidPaginationWithEmptyItems() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + + @Test + public void testInvalidPaginationNullItems() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + + @Test + public void testInvalidPaginationNullPage() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn(null); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + @Test public void testLinkPaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new LinkPagination("$response.body#/next_link"))); + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/next_link"))); + } + + @Test + public void testInvalidLinkPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/INVALID")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); } @Test public void testCursorPaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new CursorPagination("$response.body#/page_info", + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new CursorPagination("$response.body#/page_info", "$request.path#/cursor"))); } + @Test + public void testInvalidCursorPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new CursorPagination("$response.body#/INVALID", "$request.path#/cursor")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + + paginatedData = getPaginatedData(call1, null, null, + new CursorPagination("$response.body#/page_info", "")); + pages = paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + @Test public void testOffsetPaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new OffsetPagination("$request.headers#/offset"))); + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new OffsetPagination("$request.headers#/offset"))); + } + + @Test + public void testInvalidOffsetPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new OffsetPagination(null)); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); } @Test public void testPagePaginationData() throws IOException, CoreApiException { - verifyData(getPaginatedApiCall(new PagePagination("$request.query#/page"))); + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new PagePagination("$request.query#/page"))); } - private void verifyData(PaginatedData, RecordPage, CoreApiException> paginatedData) { - int index = 0; - List expectedData = Arrays.asList( - "apple", "mango", "orange", "potato", "carrot", "tomato"); + @Test + public void testInvalidPagePaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new PagePagination("$request.query#/INVALID")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + + @Test + public void testMultiPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/INVALID"), + // invalid next link pointer so 2nd call will be made using page pagination + new PagePagination("$request.body#/limit"))); + } + + @Test + public void testInvalidPaginationDataAsync() + throws IOException, CoreApiException, InterruptedException, ExecutionException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new PagePagination("$request.INVALID#/page")); + + boolean hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(Arrays.asList("apple", "mango", "orange"), + paginatedData.getPage(cs -> cs).get().getItems()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertFalse(hasNext); + } - Iterator itemIterator = paginatedData.items(cs -> { + @Test + public void testMultiPaginationDataAsync() + throws IOException, CoreApiException, InterruptedException, ExecutionException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/INVALID"), + // invalid next link pointer so 2nd call will be made using page pagination + new PagePagination("$request.body#/limit")); + + Function, String> itemCreator = cs -> { try { return cs.get(); } catch (CoreApiException | IOException e) { return null; } - }); + }; + Function, CoreApiException>, + PageWrapper> pageCreator = cs -> { + try { + return cs.get(); + } catch (CoreApiException | IOException e) { + return null; + } + }; + + RecordPage expectedPage1 = new RecordPage(); + expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); + expectedPage1.setPageInfo("fruits"); + expectedPage1.setNextLink("https://localhost:3000/path?page=2"); + + RecordPage expectedPage2 = new RecordPage(); + expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); + expectedPage2.setPageInfo("vegitables"); + expectedPage2.setNextLink("https://localhost:3000/path?page=3"); + + PageWrapper page = paginatedData.getPage(pageCreator); + assertNull(page); + assertTrue(paginatedData.getItems(itemCreator).size() == 0); + + boolean hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); + page = paginatedData.getPage(pageCreator); + assertEquals(expectedPage1.getData(), page.getResult().getData()); + assertEquals(expectedPage1.getNextLink(), page.getResult().getNextLink()); + assertEquals(expectedPage1.getPageInfo(), page.getResult().getPageInfo()); + assertEquals("/path/cursor?page=1", page.getNextLinkInput()); + assertEquals(-1, page.getPageInput()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); + page = paginatedData.getPage(pageCreator); + assertEquals(expectedPage2.getData(), page.getResult().getData()); + assertEquals(expectedPage2.getNextLink(), page.getResult().getNextLink()); + assertEquals(expectedPage2.getPageInfo(), page.getResult().getPageInfo()); + assertNull(page.getNextLinkInput()); + assertEquals(2, page.getPageInput()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertFalse(hasNext); + page = paginatedData.getPage(pageCreator); + assertNull(page); + assertTrue(paginatedData.getItems(itemCreator).size() == 0); + } + + private void verifyData(PaginatedData, + RecordPage, CoreApiException> paginatedData) throws CoreApiException, IOException { + int index = 0; + List expectedItems = Arrays.asList( + "apple", "mango", "orange", "potato", "carrot", "tomato"); + + Iterator> itemIterator = + paginatedData.items(cs -> cs); while (itemIterator.hasNext()) { - String d = itemIterator.next(); + String d = itemIterator.next().get(); assertNotNull(d); - assertEquals(expectedData.get(index), d); + assertEquals(expectedItems.get(index), d); index++; } - Exception exception = assertThrows(NoSuchElementException.class, () -> { - itemIterator.next(); - }); + Exception exception = assertThrows(NoSuchElementException.class, itemIterator::next); assertEquals("No more items available.", exception.getMessage()); RecordPage expectedPage1 = new RecordPage(); @@ -222,25 +487,47 @@ private void verifyData(PaginatedData, R RecordPage expectedPage2 = new RecordPage(); expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); expectedPage2.setPageInfo("vegitables"); - expectedPage2.setNextLink(null); + expectedPage2.setNextLink("https://localhost:3000/path?page=3"); - int pageNum = 0; + int pageOffset = 0; List expectedPages = Arrays.asList(expectedPage1, expectedPage2); - Iterator> pagesIterator = paginatedData.pages(cs -> { - try { - return cs.get(); - } catch (CoreApiException | IOException e) { - return null; - } - }); + Iterator, CoreApiException>> pagesIterator = + paginatedData.pages(cs -> cs); + while (pagesIterator.hasNext()) { - PageWrapper p = pagesIterator.next(); + PageWrapper p = pagesIterator.next().get(); assertNotNull(p); - assertEquals(expectedPages.get(pageNum).getData(), p.getResult().getData()); - assertEquals(expectedPages.get(pageNum).getPageInfo(), p.getResult().getPageInfo()); - assertEquals(expectedPages.get(pageNum).getNextLink(), p.getResult().getNextLink()); - pageNum++; + + String expectedPageInfo = expectedPages.get(pageOffset).getPageInfo(); + String expectedNextLink = expectedPages.get(pageOffset).getNextLink(); + List expectedData = expectedPages.get(pageOffset).getData(); + + assertEquals(expectedPageInfo, p.getResult().getPageInfo()); + assertEquals(expectedNextLink, p.getResult().getNextLink()); + assertEquals(expectedData, p.getResult().getData()); + assertEquals(expectedData, p.getItems()); + assertEquals(200, p.getStatusCode()); + assertEquals(getHttpHeaders(), p.getHeaders()); + if (p.getCursorInput() != null && pageOffset > 0) { + assertEquals(expectedPages.get(pageOffset - 1).getPageInfo(), p.getCursorInput()); + } + + if (p.getNextLinkInput() != null && pageOffset > 0) { + assertEquals(expectedPages.get(pageOffset - 1).getNextLink(), p.getNextLinkInput()); + } + + if (p.getPageInput() != -1) { + assertEquals(pageOffset + 1, p.getPageInput()); + } + + if (p.getOffsetInput() != -1) { + assertEquals(pageOffset * 3, p.getOffsetInput()); + } + pageOffset++; } + + exception = assertThrows(NoSuchElementException.class, pagesIterator::next); + assertEquals("No more pages available.", exception.getMessage()); } /** @@ -504,27 +791,24 @@ private ApiCall getApiCall() throws IOException { .build(); } - private PaginatedData, RecordPage, CoreApiException> getPaginatedApiCall( - PaginationStrategy... pagination) throws IOException { + private PaginatedData, + RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, + Runnable call3, + PaginationStrategy... pagination) throws IOException { when(response.getHeaders()).thenReturn(getHttpHeaders()); Callback pageCallback = new Callback() { private int callNumber = 1; @Override public void onBeforeRequest(Request request) { if (callNumber == 1) { - when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + call1.run(); } else if (callNumber == 2) { - when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"}"); + call2.run(); } else if (callNumber == 3) { - when(response.getBody()).thenReturn( - "{\"data\":[]}"); + call3.run(); + callNumber = 0; } callNumber++; } @@ -539,9 +823,11 @@ public void onAfterResponse(Context context) { .path("/path/{cursor}") .templateParam(param -> param.key("cursor").value("cursor") .isRequired(false)) - .headerParam(param -> param.key("offset").value(1).isRequired(false)) + .headerParam(param -> param.key("offset").value(0).isRequired(false)) + .formParam(param -> param.key("limit").value(1).isRequired(false)) .queryParam(param -> param.key("page").value(1).isRequired(false)) - .formParam(param -> param.key("limit").value("limit").isRequired(false)) + .headerParam(param -> param.key("size").value(2).isRequired(false)) + .headerParam(param -> param.key("size").value(3).isRequired(false)) .headerParam(param -> param.key("accept").value("application/json")) .httpMethod(Method.GET)) .responseHandler(responseHandler -> responseHandler @@ -552,7 +838,7 @@ public void onAfterResponse(Context context) { param -> param.arraySerializationFormat(ArraySerializationFormat.INDEXED) .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) .build() - .paginate(p -> p, pw -> pw, r -> r.getData(), pagination); + .paginate(pd -> pd, pw -> pw, p -> p.getData(), pagination); } private ApiCall getApiCallLocalErrorTemplate(String responseString, @@ -657,6 +943,8 @@ protected static String getBaseUri(String test) { private void prepareStub() throws IOException { when(httpClient.execute(any(Request.class), any(CoreEndpointConfiguration.class))) .thenReturn(response); + when(httpClient.executeAsync(any(Request.class), any(CoreEndpointConfiguration.class))) + .thenReturn(CompletableFuture.completedFuture(response)); when(getCompatibilityFactory().createHttpHeaders(anyMap())).thenReturn(getHttpHeaders()); when(getCompatibilityFactory().createHttpRequest(any(Method.class), nullable(StringBuilder.class), nullable(HttpHeaders.class), anyMap(), anyList())) diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 4aba429c..6523c766 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -1,11 +1,8 @@ package apimatic.core.type.pagination; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 9d5d1563..969b0231 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -1,15 +1,11 @@ package apimatic.core.type.pagination; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,7 +22,6 @@ import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.HttpHeaders; import io.apimatic.coreinterfaces.http.response.Response; -import okhttp3.Headers; /** * Unit tests for the LinkPagination class. diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 72d90aef..d0692a36 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -44,7 +43,7 @@ public void testValidOffsetHeaderReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .headerParam(h -> h.key("offset").value(INITIAL_OFFSET)); + .headerParam(h -> h.key("offset").value(INITIAL_OFFSET)); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -53,7 +52,7 @@ public void testValidOffsetHeaderReturnsTrue() { // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); Builder nextRequestBuilder = offset.apply(paginatedData); - + // Verify results assertNotNull(nextRequestBuilder); nextRequestBuilder.updateByReference("$request.headers#/offset", v -> { @@ -67,7 +66,7 @@ public void testValidOffsetTemplateReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .templateParam(t -> t.key("offset").value(INITIAL_OFFSET)); + .templateParam(t -> t.key("offset").value(INITIAL_OFFSET)); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -76,7 +75,7 @@ public void testValidOffsetTemplateReturnsTrue() { // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.path#/offset"); Builder nextRequestBuilder = offset.apply(paginatedData); - + // Verify results assertNotNull(nextRequestBuilder); nextRequestBuilder.updateByReference("$request.path#/offset", v -> { @@ -90,7 +89,7 @@ public void testValidOffsetReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(INITIAL_OFFSET)); + .queryParam(q -> q.key("offset").value(INITIAL_OFFSET)); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -99,7 +98,7 @@ public void testValidOffsetReturnsTrue() { // Test the offset pagination OffsetPagination offset = new OffsetPagination("$request.query#/offset"); Builder nextRequestBuilder = offset.apply(paginatedData); - + // Verify results assertNotNull(nextRequestBuilder); nextRequestBuilder.updateByReference("$request.query#/offset", v -> { @@ -112,12 +111,15 @@ public void testValidOffsetReturnsTrue() { public void testValidOffsetAsInnerFieldReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - Map offsetMap = new HashMap() {{ - put("val", "1"); - }}; - + Map offsetMap = new HashMap() { + private static final long serialVersionUID = 1L; + { + put("val", "1"); + } + }; + HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(offsetMap)); + .queryParam(q -> q.key("offset").value(offsetMap)); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -126,7 +128,7 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { // Test the offset pagination with nested field OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); Builder nextRequestBuilder = offset.apply(paginatedData); - + // Verify results assertNotNull(nextRequestBuilder); nextRequestBuilder.updateByReference("$request.query#/offset/val", v -> { @@ -139,8 +141,7 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { public void testValidStringOffsetReturnsTrue() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value("5")); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().queryParam(q -> q.key("offset").value("5")); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -149,7 +150,7 @@ public void testValidStringOffsetReturnsTrue() { // Test the offset pagination with string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); Builder nextRequestBuilder = offset.apply(paginatedData); - + // Verify results assertNotNull(nextRequestBuilder); nextRequestBuilder.updateByReference("$request.query#/offset", v -> { @@ -157,12 +158,12 @@ public void testValidStringOffsetReturnsTrue() { return v; }); } + @Test public void testInvalidStringOffsetReturnsFalse() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); - HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value("5a")); + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().queryParam(q -> q.key("offset").value("5a")); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -170,13 +171,13 @@ public void testInvalidStringOffsetReturnsFalse() { // Test the offset pagination with invalid string value OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - + // Verify the offset remains unchanged for invalid values Builder nextRequestBuilder = offset.apply(paginatedData); assertNotNull(nextRequestBuilder); - + nextRequestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals("5a", v); // Value should remain unchanged + assertEquals("5a", v); // Value should remain unchanged return v; }); } @@ -193,18 +194,18 @@ public void testMissingOffsetReturnsFalse() { // Test the offset pagination with missing offset OffsetPagination offset = new OffsetPagination("$request.query#/offset"); - + // Execute and verify Builder nextRequestBuilder = offset.apply(paginatedData); assertNotNull("Builder should still be returned even with missing offset", nextRequestBuilder); - + // Verify no update is attempted for missing offset AtomicBoolean callbackInvoked = new AtomicBoolean(false); nextRequestBuilder.updateByReference("$request.query#/offset", v -> { callbackInvoked.set(true); return v; }); - + assertFalse("Callback should not be invoked for missing offset", callbackInvoked.get()); } @@ -213,7 +214,7 @@ public void testNullOffsetReturnsFalse() { // Setup mocks PaginatedData paginatedData = mock(PaginatedData.class); HttpRequest.Builder requestBuilder = new HttpRequest.Builder() - .queryParam(q -> q.key("offset").value(NUMERIC_OFFSET)); + .queryParam(q -> q.key("offset").value(NUMERIC_OFFSET)); // Mock behaviors when(paginatedData.getRequestBuilder()).thenReturn(requestBuilder); @@ -221,11 +222,11 @@ public void testNullOffsetReturnsFalse() { // Test with null offset configuration OffsetPagination offset = new OffsetPagination(null); - + // Execute and verify Builder nextRequestBuilder = offset.apply(paginatedData); assertNotNull("Builder should still be returned even with null offset config", nextRequestBuilder); - + // Verify original offset remains unchanged AtomicBoolean callbackInvoked = new AtomicBoolean(false); nextRequestBuilder.updateByReference("$request.query#/offset", v -> { @@ -233,7 +234,7 @@ public void testNullOffsetReturnsFalse() { assertEquals("Original offset should remain unchanged", NUMERIC_OFFSET, v); return v; }); - + assertTrue("Should still process existing offset values", callbackInvoked.get()); } } diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index d1687eaa..a9ada7e1 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -1,9 +1,7 @@ package apimatic.core.type.pagination; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; From 26beb5c6b24f4528e106b103e3217c55ba7ade38 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Sat, 7 Jun 2025 13:37:14 +0500 Subject: [PATCH 09/33] adds 100% code coverage related to pagination --- src/test/java/apimatic/core/EndToEndTest.java | 429 +--------------- .../type/pagination/OffsetPaginationTest.java | 17 +- .../type/pagination/PaginatedDataTest.java | 483 ++++++++++++++++++ 3 files changed, 499 insertions(+), 430 deletions(-) create mode 100644 src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index 430f157f..71a819bc 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -1,11 +1,7 @@ package apimatic.core; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyMap; @@ -13,16 +9,10 @@ import static org.mockito.Mockito.when; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.function.Function; import org.junit.Before; import org.junit.Rule; @@ -33,19 +23,10 @@ import apimatic.core.exceptions.GlobalTestException; import apimatic.core.mocks.MockCoreConfig; -import apimatic.core.type.pagination.RecordPage; import io.apimatic.core.ApiCall; import io.apimatic.core.ErrorCase; import io.apimatic.core.GlobalConfiguration; import io.apimatic.core.types.CoreApiException; -import io.apimatic.core.types.pagination.CheckedSupplier; -import io.apimatic.core.types.pagination.CursorPagination; -import io.apimatic.core.types.pagination.LinkPagination; -import io.apimatic.core.types.pagination.OffsetPagination; -import io.apimatic.core.types.pagination.PagePagination; -import io.apimatic.core.types.pagination.PageWrapper; -import io.apimatic.core.types.pagination.PaginatedData; -import io.apimatic.core.types.pagination.PaginationStrategy; import io.apimatic.core.utilities.CoreHelper; import io.apimatic.coreinterfaces.http.Callback; import io.apimatic.coreinterfaces.http.Context; @@ -75,7 +56,7 @@ public class EndToEndTest extends MockCoreConfig { /** * Map of Global Error Cases */ - private static final Map> GLOBAL_ERROR_CASES = + protected static final Map> GLOBAL_ERROR_CASES = new HashMap>() { private static final long serialVersionUID = 1L; { @@ -141,7 +122,7 @@ public class EndToEndTest extends MockCoreConfig { * Mock of {@link Response}. */ @Mock - private Response response; + protected Response response; /** @@ -176,360 +157,6 @@ public void testEndToEndSyncCall() throws IOException, CoreApiException { assertEquals(actual, expected); } - @Test - public void testInvalidPaginationWithEmptyItems() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"); - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/next_link")); - assertFalse(paginatedData.pages(cs -> cs).hasNext()); - } - - @Test - public void testInvalidPaginationNullItems() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"); - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/next_link")); - assertFalse(paginatedData.pages(cs -> cs).hasNext()); - } - - @Test - public void testInvalidPaginationNullPage() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn(null); - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/next_link")); - assertFalse(paginatedData.pages(cs -> cs).hasNext()); - } - - @Test - public void testLinkPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - verifyData(getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/next_link"))); - } - - @Test - public void testInvalidLinkPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/INVALID")); - Iterator, CoreApiException>> pages = - paginatedData.pages(cs -> cs); - assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect - assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); - } - - @Test - public void testCursorPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - verifyData(getPaginatedData(call1, call2, call3, - new CursorPagination("$response.body#/page_info", - "$request.path#/cursor"))); - } - - @Test - public void testInvalidCursorPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new CursorPagination("$response.body#/INVALID", "$request.path#/cursor")); - Iterator, CoreApiException>> pages = - paginatedData.pages(cs -> cs); - assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect - assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); - - paginatedData = getPaginatedData(call1, null, null, - new CursorPagination("$response.body#/page_info", "")); - pages = paginatedData.pages(cs -> cs); - assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect - assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); - } - - @Test - public void testOffsetPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - verifyData(getPaginatedData(call1, call2, call3, - new OffsetPagination("$request.headers#/offset"))); - } - - @Test - public void testInvalidOffsetPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new OffsetPagination(null)); - Iterator, CoreApiException>> pages = - paginatedData.pages(cs -> cs); - assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect - assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); - } - - @Test - public void testPagePaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - verifyData(getPaginatedData(call1, call2, call3, - new PagePagination("$request.query#/page"))); - } - - @Test - public void testInvalidPagePaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new PagePagination("$request.query#/INVALID")); - Iterator, CoreApiException>> pages = - paginatedData.pages(cs -> cs); - assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect - assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); - } - - @Test - public void testMultiPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - verifyData(getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/INVALID"), - // invalid next link pointer so 2nd call will be made using page pagination - new PagePagination("$request.body#/limit"))); - } - - @Test - public void testInvalidPaginationDataAsync() - throws IOException, CoreApiException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new PagePagination("$request.INVALID#/page")); - - boolean hasNext = paginatedData.fetchNextPageAsync().get(); - assertTrue(hasNext); - assertEquals(Arrays.asList("apple", "mango", "orange"), - paginatedData.getPage(cs -> cs).get().getItems()); - - hasNext = paginatedData.fetchNextPageAsync().get(); - assertFalse(hasNext); - } - - @Test - public void testMultiPaginationDataAsync() - throws IOException, CoreApiException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( - "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," - + "\"page_info\":\"vegitables\"," - + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( - "{\"data\":[]}"); - - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/INVALID"), - // invalid next link pointer so 2nd call will be made using page pagination - new PagePagination("$request.body#/limit")); - - Function, String> itemCreator = cs -> { - try { - return cs.get(); - } catch (CoreApiException | IOException e) { - return null; - } - }; - Function, CoreApiException>, - PageWrapper> pageCreator = cs -> { - try { - return cs.get(); - } catch (CoreApiException | IOException e) { - return null; - } - }; - - RecordPage expectedPage1 = new RecordPage(); - expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); - expectedPage1.setPageInfo("fruits"); - expectedPage1.setNextLink("https://localhost:3000/path?page=2"); - - RecordPage expectedPage2 = new RecordPage(); - expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); - expectedPage2.setPageInfo("vegitables"); - expectedPage2.setNextLink("https://localhost:3000/path?page=3"); - - PageWrapper page = paginatedData.getPage(pageCreator); - assertNull(page); - assertTrue(paginatedData.getItems(itemCreator).size() == 0); - - boolean hasNext = paginatedData.fetchNextPageAsync().get(); - assertTrue(hasNext); - assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); - page = paginatedData.getPage(pageCreator); - assertEquals(expectedPage1.getData(), page.getResult().getData()); - assertEquals(expectedPage1.getNextLink(), page.getResult().getNextLink()); - assertEquals(expectedPage1.getPageInfo(), page.getResult().getPageInfo()); - assertEquals("/path/cursor?page=1", page.getNextLinkInput()); - assertEquals(-1, page.getPageInput()); - - hasNext = paginatedData.fetchNextPageAsync().get(); - assertTrue(hasNext); - assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); - page = paginatedData.getPage(pageCreator); - assertEquals(expectedPage2.getData(), page.getResult().getData()); - assertEquals(expectedPage2.getNextLink(), page.getResult().getNextLink()); - assertEquals(expectedPage2.getPageInfo(), page.getResult().getPageInfo()); - assertNull(page.getNextLinkInput()); - assertEquals(2, page.getPageInput()); - - hasNext = paginatedData.fetchNextPageAsync().get(); - assertFalse(hasNext); - page = paginatedData.getPage(pageCreator); - assertNull(page); - assertTrue(paginatedData.getItems(itemCreator).size() == 0); - } - - private void verifyData(PaginatedData, - RecordPage, CoreApiException> paginatedData) throws CoreApiException, IOException { - int index = 0; - List expectedItems = Arrays.asList( - "apple", "mango", "orange", "potato", "carrot", "tomato"); - - Iterator> itemIterator = - paginatedData.items(cs -> cs); - while (itemIterator.hasNext()) { - String d = itemIterator.next().get(); - assertNotNull(d); - assertEquals(expectedItems.get(index), d); - index++; - } - - Exception exception = assertThrows(NoSuchElementException.class, itemIterator::next); - assertEquals("No more items available.", exception.getMessage()); - - RecordPage expectedPage1 = new RecordPage(); - expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); - expectedPage1.setPageInfo("fruits"); - expectedPage1.setNextLink("https://localhost:3000/path?page=2"); - - RecordPage expectedPage2 = new RecordPage(); - expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); - expectedPage2.setPageInfo("vegitables"); - expectedPage2.setNextLink("https://localhost:3000/path?page=3"); - - int pageOffset = 0; - List expectedPages = Arrays.asList(expectedPage1, expectedPage2); - Iterator, CoreApiException>> pagesIterator = - paginatedData.pages(cs -> cs); - - while (pagesIterator.hasNext()) { - PageWrapper p = pagesIterator.next().get(); - assertNotNull(p); - - String expectedPageInfo = expectedPages.get(pageOffset).getPageInfo(); - String expectedNextLink = expectedPages.get(pageOffset).getNextLink(); - List expectedData = expectedPages.get(pageOffset).getData(); - - assertEquals(expectedPageInfo, p.getResult().getPageInfo()); - assertEquals(expectedNextLink, p.getResult().getNextLink()); - assertEquals(expectedData, p.getResult().getData()); - assertEquals(expectedData, p.getItems()); - assertEquals(200, p.getStatusCode()); - assertEquals(getHttpHeaders(), p.getHeaders()); - if (p.getCursorInput() != null && pageOffset > 0) { - assertEquals(expectedPages.get(pageOffset - 1).getPageInfo(), p.getCursorInput()); - } - - if (p.getNextLinkInput() != null && pageOffset > 0) { - assertEquals(expectedPages.get(pageOffset - 1).getNextLink(), p.getNextLinkInput()); - } - - if (p.getPageInput() != -1) { - assertEquals(pageOffset + 1, p.getPageInput()); - } - - if (p.getOffsetInput() != -1) { - assertEquals(pageOffset * 3, p.getOffsetInput()); - } - pageOffset++; - } - - exception = assertThrows(NoSuchElementException.class, pagesIterator::next); - assertEquals("No more pages available.", exception.getMessage()); - } - /** * Test the local error template. * @throws IOException Signals that an I/O exception of some sort has occurred. @@ -791,56 +418,6 @@ private ApiCall getApiCall() throws IOException { .build(); } - private PaginatedData, - RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, - Runnable call3, - PaginationStrategy... pagination) throws IOException { - when(response.getHeaders()).thenReturn(getHttpHeaders()); - Callback pageCallback = new Callback() { - private int callNumber = 1; - @Override - public void onBeforeRequest(Request request) { - if (callNumber == 1) { - call1.run(); - } - else if (callNumber == 2) { - call2.run(); - } - else if (callNumber == 3) { - call3.run(); - callNumber = 0; - } - callNumber++; - } - @Override - public void onAfterResponse(Context context) { - // TODO Auto-generated method stub - } - }; - return new ApiCall.Builder() - .globalConfig(getGlobalConfig(pageCallback)) - .requestBuilder(requestBuilder -> requestBuilder.server("https://localhost:3000") - .path("/path/{cursor}") - .templateParam(param -> param.key("cursor").value("cursor") - .isRequired(false)) - .headerParam(param -> param.key("offset").value(0).isRequired(false)) - .formParam(param -> param.key("limit").value(1).isRequired(false)) - .queryParam(param -> param.key("page").value(1).isRequired(false)) - .headerParam(param -> param.key("size").value(2).isRequired(false)) - .headerParam(param -> param.key("size").value(3).isRequired(false)) - .headerParam(param -> param.key("accept").value("application/json")) - .httpMethod(Method.GET)) - .responseHandler(responseHandler -> responseHandler - .deserializer(res -> CoreHelper.deserialize(res, RecordPage.class)) - .nullify404(false) - .globalErrorCase(Collections.emptyMap())) - .endpointConfiguration( - param -> param.arraySerializationFormat(ArraySerializationFormat.INDEXED) - .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) - .build() - .paginate(pd -> pd, pw -> pw, p -> p.getData(), pagination); - } - private ApiCall getApiCallLocalErrorTemplate(String responseString, int statusCode) throws IOException { when(response.getBody()).thenReturn(responseString); @@ -924,7 +501,7 @@ private ApiCall getApiCallGlobalErrorTemplateWithHeade .build(); } - private GlobalConfiguration getGlobalConfig(Callback callback) { + protected GlobalConfiguration getGlobalConfig(Callback callback) { String userAgent = "APIMATIC 3.0"; GlobalConfiguration globalConfig = new GlobalConfiguration.Builder() .authentication(Collections.emptyMap()) diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index e6d0e2d2..d3fa5cf1 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -169,7 +168,12 @@ public void testInvalidStringOffsetReturnsFalse() { OffsetPagination offset = new OffsetPagination("$request.query#/offset"); Builder requestBuilder = offset.apply(paginatedData); - assertNull(requestBuilder); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals("5a", v); + return v; + }); } @Test @@ -182,7 +186,7 @@ public void testMissingOffsetReturnsFalse() { OffsetPagination offset = new OffsetPagination("$request.query#/offset"); Builder requestBuilder = offset.apply(paginatedData); - assertNull(requestBuilder); + assertNotNull(requestBuilder); } @Test @@ -197,6 +201,11 @@ public void testNullOffsetReturnsFalse() { OffsetPagination offset = new OffsetPagination(null); Builder requestBuilder = offset.apply(paginatedData); - assertNull(requestBuilder); + assertNotNull(requestBuilder); + + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals(NUMERIC_OFFSET, v); + return v; + }); } } \ No newline at end of file diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java new file mode 100644 index 00000000..54165465 --- /dev/null +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -0,0 +1,483 @@ +package apimatic.core.type.pagination; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; + +import org.junit.Test; + +import apimatic.core.EndToEndTest; +import io.apimatic.core.ApiCall; +import io.apimatic.core.types.CoreApiException; +import io.apimatic.core.types.pagination.CheckedSupplier; +import io.apimatic.core.types.pagination.CursorPagination; +import io.apimatic.core.types.pagination.LinkPagination; +import io.apimatic.core.types.pagination.OffsetPagination; +import io.apimatic.core.types.pagination.PagePagination; +import io.apimatic.core.types.pagination.PageWrapper; +import io.apimatic.core.types.pagination.PaginatedData; +import io.apimatic.core.types.pagination.PaginationStrategy; +import io.apimatic.core.utilities.CoreHelper; +import io.apimatic.coreinterfaces.http.Callback; +import io.apimatic.coreinterfaces.http.Context; +import io.apimatic.coreinterfaces.http.Method; +import io.apimatic.coreinterfaces.http.request.ArraySerializationFormat; +import io.apimatic.coreinterfaces.http.request.Request; +import io.apimatic.coreinterfaces.http.request.configuration.RetryOption; + +public class PaginatedDataTest extends EndToEndTest { + + @Test + public void testInvalidPaginationWithFailingResponse() throws IOException { + Runnable call1 = () -> { + when(response.getStatusCode()).thenReturn(404); + }; + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + Exception exception = assertThrows(CoreApiException.class, pages.next()::get); + assertEquals("Not found", exception.getMessage()); + assertFalse(pages.hasNext()); + } + + @Test + public void testInvalidPaginationWithFailingResponseAsync() + throws IOException, InterruptedException, ExecutionException { + Runnable call1 = () -> { + when(response.getStatusCode()).thenReturn(404); + }; + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + + boolean hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + Exception exception = assertThrows(CoreApiException.class, + paginatedData.getPage(cs -> cs)::get); + assertEquals("Not found", exception.getMessage()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertFalse(hasNext); + } + + @Test + public void testInvalidPaginationWithEmptyItems() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + + @Test + public void testInvalidPaginationNullItems() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + + @Test + public void testInvalidPaginationNullPage() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn(null); + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/next_link")); + assertFalse(paginatedData.pages(cs -> cs).hasNext()); + } + + @Test + public void testLinkPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/next_link"))); + } + + @Test + public void testInvalidLinkPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new LinkPagination("$response.body#/INVALID")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + + @Test + public void testCursorPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new CursorPagination("$response.body#/page_info", + "$request.path#/cursor"))); + } + + @Test + public void testInvalidCursorPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new CursorPagination("$response.body#/INVALID", "$request.path#/cursor")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + + paginatedData = getPaginatedData(call1, null, null, + new CursorPagination("$response.body#/page_info", "")); + pages = paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + + @Test + public void testOffsetPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new OffsetPagination("$request.headers#/offset"))); + } + + @Test + public void testInvalidOffsetPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new OffsetPagination(null)); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + + @Test + public void testPagePaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new PagePagination("$request.query#/page"))); + } + + @Test + public void testInvalidPagePaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new PagePagination("$request.query#/INVALID")); + Iterator, CoreApiException>> pages = + paginatedData.pages(cs -> cs); + assertTrue(pages.hasNext()); + assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); + assertFalse(pages.hasNext()); + } + + @Test + public void testMultiPaginationData() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + verifyData(getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/INVALID"), + // invalid next link pointer so 2nd call will be made using page pagination + new PagePagination("$request.body#/limit"))); + } + + @Test + public void testInvalidPaginationDataAsync() + throws IOException, CoreApiException, InterruptedException, ExecutionException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + new PagePagination("$request.INVALID#/page")); + + boolean hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(Arrays.asList("apple", "mango", "orange"), + paginatedData.getPage(cs -> cs).get().getItems()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertFalse(hasNext); + } + + @Test + public void testMultiPaginationDataAsync() + throws IOException, CoreApiException, InterruptedException, ExecutionException { + Runnable call1 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + + "page_info\":\"fruits\"," + + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + Runnable call2 = () -> when(response.getBody()).thenReturn( + "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + + "\"page_info\":\"vegitables\"," + + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); + Runnable call3 = () -> when(response.getBody()).thenReturn( + "{\"data\":[]}"); + + PaginatedData, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, call2, call3, + new LinkPagination("$response.body#/INVALID"), + // invalid next link pointer so 2nd call will be made using page pagination + new PagePagination("$request.body#/limit")); + + Function, String> itemCreator = cs -> { + try { + return cs.get(); + } catch (CoreApiException | IOException e) { + return null; + } + }; + Function, CoreApiException>, + PageWrapper> pageCreator = cs -> { + try { + return cs.get(); + } catch (CoreApiException | IOException e) { + return null; + } + }; + + RecordPage expectedPage1 = new RecordPage(); + expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); + expectedPage1.setPageInfo("fruits"); + expectedPage1.setNextLink("https://localhost:3000/path?page=2"); + + RecordPage expectedPage2 = new RecordPage(); + expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); + expectedPage2.setPageInfo("vegitables"); + expectedPage2.setNextLink("https://localhost:3000/path?page=3"); + + PageWrapper page = paginatedData.getPage(pageCreator); + assertNull(page); + assertTrue(paginatedData.getItems(itemCreator).size() == 0); + + boolean hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); + page = paginatedData.getPage(pageCreator); + assertEquals(expectedPage1.getData(), page.getResult().getData()); + assertEquals(expectedPage1.getNextLink(), page.getResult().getNextLink()); + assertEquals(expectedPage1.getPageInfo(), page.getResult().getPageInfo()); + assertEquals("/path/cursor?page=1", page.getNextLinkInput()); + assertEquals(-1, page.getPageInput()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertTrue(hasNext); + assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); + page = paginatedData.getPage(pageCreator); + assertEquals(expectedPage2.getData(), page.getResult().getData()); + assertEquals(expectedPage2.getNextLink(), page.getResult().getNextLink()); + assertEquals(expectedPage2.getPageInfo(), page.getResult().getPageInfo()); + assertNull(page.getNextLinkInput()); + assertEquals(2, page.getPageInput()); + + hasNext = paginatedData.fetchNextPageAsync().get(); + assertFalse(hasNext); + page = paginatedData.getPage(pageCreator); + assertNull(page); + assertTrue(paginatedData.getItems(itemCreator).size() == 0); + } + + private void verifyData(PaginatedData, + RecordPage, CoreApiException> paginatedData) throws CoreApiException, IOException { + int index = 0; + List expectedItems = Arrays.asList( + "apple", "mango", "orange", "potato", "carrot", "tomato"); + + Iterator> itemIterator = + paginatedData.items(cs -> cs); + while (itemIterator.hasNext()) { + String d = itemIterator.next().get(); + assertNotNull(d); + assertEquals(expectedItems.get(index), d); + index++; + } + + Exception exception = assertThrows(NoSuchElementException.class, itemIterator::next); + assertEquals("No more items available.", exception.getMessage()); + + RecordPage expectedPage1 = new RecordPage(); + expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); + expectedPage1.setPageInfo("fruits"); + expectedPage1.setNextLink("https://localhost:3000/path?page=2"); + + RecordPage expectedPage2 = new RecordPage(); + expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); + expectedPage2.setPageInfo("vegitables"); + expectedPage2.setNextLink("https://localhost:3000/path?page=3"); + + int pageOffset = 0; + List expectedPages = Arrays.asList(expectedPage1, expectedPage2); + Iterator, CoreApiException>> pagesIterator = + paginatedData.pages(cs -> cs); + + while (pagesIterator.hasNext()) { + PageWrapper p = pagesIterator.next().get(); + assertNotNull(p); + + String expectedPageInfo = expectedPages.get(pageOffset).getPageInfo(); + String expectedNextLink = expectedPages.get(pageOffset).getNextLink(); + List expectedData = expectedPages.get(pageOffset).getData(); + + assertEquals(expectedPageInfo, p.getResult().getPageInfo()); + assertEquals(expectedNextLink, p.getResult().getNextLink()); + assertEquals(expectedData, p.getResult().getData()); + assertEquals(expectedData, p.getItems()); + assertEquals(200, p.getStatusCode()); + assertEquals(getHttpHeaders(), p.getHeaders()); + if (p.getCursorInput() != null && pageOffset > 0) { + assertEquals(expectedPages.get(pageOffset - 1).getPageInfo(), p.getCursorInput()); + } + + if (p.getNextLinkInput() != null && pageOffset > 0) { + assertEquals(expectedPages.get(pageOffset - 1).getNextLink(), p.getNextLinkInput()); + } + + if (p.getPageInput() != -1) { + assertEquals(pageOffset + 1, p.getPageInput()); + } + + if (p.getOffsetInput() != -1) { + assertEquals(pageOffset * 3, p.getOffsetInput()); + } + pageOffset++; + } + + exception = assertThrows(NoSuchElementException.class, pagesIterator::next); + assertEquals("No more pages available.", exception.getMessage()); + } + + private PaginatedData, + RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, + Runnable call3, + PaginationStrategy... pagination) throws IOException { + when(response.getHeaders()).thenReturn(getHttpHeaders()); + Callback pageCallback = new Callback() { + private int callNumber = 1; + @Override + public void onBeforeRequest(Request request) { + if (callNumber == 1) { + call1.run(); + } + else if (callNumber == 2) { + call2.run(); + } + else if (callNumber == 3) { + call3.run(); + callNumber = 0; + } + callNumber++; + } + @Override + public void onAfterResponse(Context context) { + // TODO Auto-generated method stub + } + }; + return new ApiCall.Builder() + .globalConfig(getGlobalConfig(pageCallback)) + .requestBuilder(requestBuilder -> requestBuilder.server("https://localhost:3000") + .path("/path/{cursor}") + .templateParam(param -> param.key("cursor").value("cursor") + .isRequired(false)) + .headerParam(param -> param.key("offset").value(0).isRequired(false)) + .formParam(param -> param.key("limit").value(1).isRequired(false)) + .queryParam(param -> param.key("page").value(1).isRequired(false)) + .headerParam(param -> param.key("size").value(2).isRequired(false)) + .headerParam(param -> param.key("size").value(3).isRequired(false)) + .headerParam(param -> param.key("accept").value("application/json")) + .httpMethod(Method.GET)) + .responseHandler(responseHandler -> responseHandler + .deserializer(res -> CoreHelper.deserialize(res, RecordPage.class)) + .nullify404(false) + .globalErrorCase(GLOBAL_ERROR_CASES)) + .endpointConfiguration( + param -> param.arraySerializationFormat(ArraySerializationFormat.INDEXED) + .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) + .build() + .paginate(pd -> pd, pw -> pw, p -> p.getData(), pagination); + } + +} From 2062525f01c7a5dad6017a2ee14a163a96421978 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Tue, 10 Jun 2025 12:21:33 +0500 Subject: [PATCH 10/33] minor refactoring --- .../java/apimatic/core/type/pagination/PaginatedDataTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 54165465..5ff51bfc 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -59,9 +59,7 @@ public void testInvalidPaginationWithFailingResponse() throws IOException { @Test public void testInvalidPaginationWithFailingResponseAsync() throws IOException, InterruptedException, ExecutionException { - Runnable call1 = () -> { - when(response.getStatusCode()).thenReturn(404); - }; + Runnable call1 = () -> when(response.getStatusCode()).thenReturn(404); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); From 06d05470b7e7629f50c4c16ca638f3b9a830959a Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Wed, 11 Jun 2025 13:41:28 +0500 Subject: [PATCH 11/33] test(pagination): refactor --- .../java/io/apimatic/core/HttpRequest.java | 8 +- .../apimatic/core/RequestBuilderTest.java | 15 ++ .../type/pagination/CheckedSupplierTest.java | 33 +++ .../type/pagination/CursorPaginationTest.java | 106 ++++---- .../type/pagination/LinkPaginationTest.java | 181 ++++++++++--- .../type/pagination/OffsetPaginationTest.java | 254 ++++++++++++++++-- .../type/pagination/PagePaginationTest.java | 92 ++++--- 7 files changed, 542 insertions(+), 147 deletions(-) create mode 100644 src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 8e0f0ad1..26698796 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -365,7 +365,13 @@ private void updateBodyParams(UnaryOperator setter, String point) { } if (bodySerializer != null && point == "") { - bodySerializer = () -> setter.apply(bodySerializer.supply()).toString(); + try { + String serializedBody = bodySerializer.supply(); + String newSerializedBody = setter.apply(serializedBody).toString(); + bodySerializer = () -> newSerializedBody; + } catch (IOException e) { + // Empty block + } return; } diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index a23c8a8e..edafafa7 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -155,6 +155,21 @@ public void testBodyParam() throws IOException { // verify assertEquals(coreHttpRequest.getBody(), "bodyValue"); } + + @Test + public void testCloneBodyParam() throws IOException { + // when + Request coreHttpRequest = + new HttpRequest.Builder().httpMethod(Method.PATCH) + .bodyParam(param -> param.key("body").value("bodyValue")).copy() + .build(getMockGlobalConfig()); + + when(coreHttpRequest.getBody()).thenReturn("bodyValue"); + + // verify + assertEquals(coreHttpRequest.getBody(), "bodyValue"); + } + @Test public void testBodyParamKey1() throws IOException { diff --git a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java new file mode 100644 index 00000000..f7589a5a --- /dev/null +++ b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java @@ -0,0 +1,33 @@ +package apimatic.core.type.pagination; + +import static org.junit.Assert.*; +import org.junit.Test; + +import java.io.IOException; + +import io.apimatic.core.types.CoreApiException; +import io.apimatic.core.types.pagination.CheckedSupplier; + +public class CheckedSupplierTest { + + @Test(expected = IOException.class) + public void testCreateErrorWithIOException() throws Exception { + IOException ioException = new IOException("Test IO Exception"); + CheckedSupplier supplier = CheckedSupplier.CreateError(ioException); + supplier.get(); // Should throw IOException + } + + @Test(expected = CoreApiException.class) + public void testCreateErrorWithCoreApiException() throws Exception { + CoreApiException apiException = new CoreApiException("Test API Exception"); + CheckedSupplier supplier = CheckedSupplier.CreateError(apiException); + supplier.get(); // Should throw CoreApiException + } + + @Test + public void testCreateErrorWithUnsupportedException() { + RuntimeException runtimeException = new RuntimeException("Test Exception"); + CheckedSupplier supplier = CheckedSupplier.CreateError(runtimeException); + assertNull(supplier); // Should return null + } +} diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 6523c766..101d7202 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -14,6 +14,7 @@ import io.apimatic.core.HttpRequest; import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.CursorPagination; +import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.response.Response; @@ -29,13 +30,21 @@ public class CursorPaginationTest { @Rule public MockitoRule initRule = MockitoJUnit.rule().silent(); + // Constants private static final String CURSOR_KEY = "cursor"; + private static final String RESPONSE_POINTER_VALID = "$response.body#/next_cursor"; + private static final String RESPONSE_POINTER_INVALID = "$response.body#/next"; + private static final String REQUEST_QUERY_POINTER = "$request.query#/cursor"; + private static final String REQUEST_HEADER_POINTER = "$request.headers#/cursor"; + private static final String REQUEST_POINTER_INVALID = "$request.headers#/next_cursor"; + private static final String NEXT_CURSOR_JSON_STRING = "{\"next_cursor\": \"xyz123\"}"; + private static final String NEXT_CURSOR_JSON_INT = "{\"next_cursor\": 123}"; /** * Test with a valid cursor value. */ @Test - public void testWithValidCursorReturnsTrue() { + public void testWithValidCursor() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -43,25 +52,27 @@ public void testWithValidCursorReturnsTrue() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.query#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { assertEquals("xyz123", v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + cursor.addMetaData(pageWrapper); + assertEquals("xyz123", pageWrapper.getCursorInput()); } /** * Test with a valid cursor but different response type (integer). */ @Test - public void testWithValidCursorAndDifferentTypeReturnsTrueA() { + public void testWithValidCursorAndDifferentTypeA() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -69,68 +80,74 @@ public void testWithValidCursorAndDifferentTypeReturnsTrueA() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.query#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + cursor.addMetaData(pageWrapper); + assertEquals("123", pageWrapper.getCursorInput()); } - + /** + * Test with a valid cursor but different response type (integer) and integer cursor input. + */ @Test - public void testWithValidCursorAndDifferentTypeReturnsTrueB() { + public void testWithValidCursorAndDifferentType() { PaginatedData paginatedData = mock(PaginatedData.class); final int current = 456; Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("cursor").value(current))); + q -> q.key(CURSOR_KEY).value(current))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.query#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + cursor.addMetaData(pageWrapper); + assertEquals("123", pageWrapper.getCursorInput()); } - + /** + * Test with valid cursor but missing in the first request. + */ @Test - public void testWithValidCursorButMissingInFirstRequestReturnsFalse() { + public void testWithValidCursorButMissingInFirstRequest() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.query#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); } - /** * Test with valid cursor from a response body and different type. */ @Test - public void testWithValidCursorFromResponseBodyReturnsTrue() { + public void testWithValidCursorFromResponseBody() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -138,26 +155,27 @@ public void testWithValidCursorFromResponseBodyReturnsTrue() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": 123}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.query#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/cursor", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + cursor.addMetaData(pageWrapper); + assertEquals("123", pageWrapper.getCursorInput()); } - /** * Test case where the response pointer is invalid. */ @Test - public void testWithInvalidResponsePointerReturnsFalse() { + public void testWithInvalidResponsePointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -165,21 +183,19 @@ public void testWithInvalidResponsePointerReturnsFalse() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination("$response.body#/next", // invalid pointer - "$request.headers#/cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_INVALID, REQUEST_HEADER_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); } - /** * Test with missing response pointer. */ @Test - public void testWithMissingResponsePointerReturnsFalse() { + public void testWithMissingResponsePointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -187,20 +203,19 @@ public void testWithMissingResponsePointerReturnsFalse() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination(null, "$request.headers#/cursor"); + CursorPagination cursor = new CursorPagination(null, REQUEST_HEADER_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); } - /** * Test case with invalid request pointer. */ @Test - public void testWithInvalidRequestPointerReturnsFalse() { + public void testWithInvalidRequestPointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -208,20 +223,18 @@ public void testWithInvalidRequestPointerReturnsFalse() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", - "$request.headers#/next_cursor"); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_POINTER_INVALID); assertNull(cursor.apply(paginatedData)); } - /** * Test with missing request pointer. */ @Test - public void testWithMissingRequestPointerReturnsFalse() { + public void testWithMissingRequest() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -229,11 +242,10 @@ public void testWithMissingRequestPointerReturnsFalse() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next_cursor\": \"xyz123\"}"); + when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination("$response.body#/next_cursor", null); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, null); assertNull(cursor.apply(paginatedData)); } - } diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 969b0231..f18e225e 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -19,6 +19,7 @@ import io.apimatic.core.HttpRequest; import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.LinkPagination; +import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.HttpHeaders; import io.apimatic.coreinterfaces.http.response.Response; @@ -28,6 +29,18 @@ */ public class LinkPaginationTest { + private static final String RESPONSE_BODY_POINTER = "$response.body#/next"; + private static final String RESPONSE_HEADERS_POINTER = "$response.headers#/next"; + private static final String REQUEST_QUERY_PAGE = "$request.query#/page"; + private static final String REQUEST_QUERY_SIZE = "$request.query#/size"; + private static final String REQUEST_HEADERS_PAGE = "$request.headers#/page"; + private static final String NEXT_URL_SINGLE = "https://api.example.com?page=2"; + private static final String NEXT_URL_MULTIPLE = "https://api.example.com?page=2&size=5"; + private static final String NEXT_URL_ENCODED = "https://api.example.com?page%20o=2%20a&size%20q=5^%214$#"; + private static final String PAGE = "page"; + private static final String SIZE = "size"; + private static final String NEXT = "next"; + /** * Silent rule for Mockito initialization. */ @@ -35,180 +48,261 @@ public class LinkPaginationTest { public MockitoRule initRule = MockitoJUnit.rule().silent(); @Test - public void testValidLinkReturnsTrue() { + public void testValidLink() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); - LinkPagination link = new LinkPagination("$response.body#/next"); + LinkPagination link = new LinkPagination(RESPONSE_BODY_POINTER); Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); + + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + link.addMetaData(pageWrapper); + assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @Test - public void testValidLinkWithAdditionalParamsReturnsTrue() { + public void testValidLinkWithAdditionalParams() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); final int pageSize = 456; when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder() - .queryParam(q -> q.key("size").value(pageSize)) - .queryParam(q -> q.key("page").value(1)) - .headerParam(h -> h.key("page").value(2))); + .queryParam(q -> q.key(SIZE).value(pageSize)) + .queryParam(q -> q.key(PAGE).value(1)) + .headerParam(h -> h.key(PAGE).value(2))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); - LinkPagination link = new LinkPagination("$response.body#/next"); + LinkPagination link = new LinkPagination(RESPONSE_BODY_POINTER); Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); - requestBuilder.updateByReference("$request.query#/size", v -> { + requestBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { assertEquals(pageSize, v); return v; }); - requestBuilder.updateByReference("$request.headers#/page", v -> { + requestBuilder.updateByReference(REQUEST_HEADERS_PAGE, v -> { assertEquals(2, v); return v; }); + + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + link.addMetaData(pageWrapper); + assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @Test - public void testValidLinkFromHeaderReturnsTrue() { - // Setup mocks + public void testValidLinkFromHeader() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); Map headers = new HashMap<>(); - headers.put("next", "https://api.example.com?page=2"); + headers.put(NEXT, NEXT_URL_SINGLE); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); when(response.getHeaders()).thenReturn(createHttpHeaders(headers)); - // Test the link pagination - LinkPagination link = new LinkPagination("$response.headers#/next"); + LinkPagination link = new LinkPagination(RESPONSE_HEADERS_POINTER); Builder requestBuilder = link.apply(paginatedData); - // Verify results assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals("2", v); // Page extracted from URL query param + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + assertEquals("2", v); return v; }); + + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + link.addMetaData(pageWrapper); + assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } private HttpHeaders createHttpHeaders(Map headers) { + return new HttpHeaders() { + + @Override + public List values(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String value(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List remove(String headerName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set names() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean has(String headerName) { + // TODO Auto-generated method stub + return false; + } + + @Override + public Map asSimpleMap() { + // TODO Auto-generated method stub + return headers; + } + + @Override + public Map> asMultimap() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addAllFromMultiMap(Map> headers) { + // TODO Auto-generated method stub + + } + + @Override + public void addAllFromMap(Map headers) { + // TODO Auto-generated method stub + + } + + @Override + public void addAll(HttpHeaders headers) { + // TODO Auto-generated method stub + + } + + @Override + public void add(String headerName, List values) { + // TODO Auto-generated method stub + + } + + @Override + public void add(String headerName, String value) { + // TODO Auto-generated method stub + + } + }; + } - @Test - public void testInvalidPointerReturnsFalse() { + public void testInvalidPointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); LinkPagination link = new LinkPagination("$response.body#/next/href"); - // Since pointer is invalid, apply(...) should return null assertNull(link.apply(paginatedData)); } - @Test - public void testMissingResponseReturnsFalse() { + public void testMissingResponse() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -218,64 +312,64 @@ public void testMissingResponseReturnsFalse() { LinkPagination link = new LinkPagination("$response.body#/next/href"); - // Since response body is null, apply should return null assertNull(link.apply(paginatedData)); } - @Test - public void testMissingPointerReturnsFalse() { + public void testMissingPointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"https://api.example.com?page=2\"}"); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); LinkPagination link = new LinkPagination(null); - // Pointer is null, apply should return null assertNull(link.apply(paginatedData)); } @Test - public void testMultipleQueryParamsReturnsTrue() { + public void testMultipleQueryParams() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()) - .thenReturn("{\"next\": \"https://api.example.com?page=2&size=5\"}"); + .thenReturn("{\"next\": \"" + NEXT_URL_MULTIPLE + "\"}"); - LinkPagination link = new LinkPagination("$response.body#/next"); + LinkPagination link = new LinkPagination(RESPONSE_BODY_POINTER); Builder nextBuilder = link.apply(paginatedData); assertNotNull(nextBuilder); - nextBuilder.updateByReference("$request.query#/page", v -> { + nextBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); - nextBuilder.updateByReference("$request.query#/size", v -> { + nextBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { assertEquals("5", v); return v; }); - } + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + link.addMetaData(pageWrapper); + assertEquals(NEXT_URL_MULTIPLE, pageWrapper.getNextLinkInput()); + } @Test - public void testEncodedQueryParamsReturnsTrue() { + public void testEncodedQueryParams() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()) - .thenReturn("{\"next\": \"https://api.example.com?page%20o=2%20a&size%20q=5^%214$#\"}"); + .thenReturn("{\"next\": \"" + NEXT_URL_ENCODED + "\"}"); - LinkPagination link = new LinkPagination("$response.body#/next"); + LinkPagination link = new LinkPagination(RESPONSE_BODY_POINTER); Builder builder = link.apply(paginatedData); assertNotNull(builder); @@ -289,6 +383,9 @@ public void testEncodedQueryParamsReturnsTrue() { assertEquals("5^!4$#", v); return v; }); - } + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + link.addMetaData(pageWrapper); + assertEquals(NEXT_URL_ENCODED, pageWrapper.getNextLinkInput()); + } } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index d3fa5cf1..606b55cb 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -2,32 +2,50 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import org.junit.Rule; import org.junit.Test; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import io.apimatic.core.GlobalConfiguration; import io.apimatic.core.HttpRequest; import io.apimatic.core.HttpRequest.Builder; +import io.apimatic.core.types.OptionalNullable.Serializer; import io.apimatic.core.types.pagination.OffsetPagination; +import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.response.Response; +import io.apimatic.coreinterfaces.type.CoreFileWrapper; /** * Unit tests for {@link OffsetPagination}. */ public class OffsetPaginationTest { private static final int INITIAL_OFFSET = 3; + private static final String INITIAL_OFFSET_STRING = "3"; private static final int PAGE_SIZE = 100; - private static final int OFFSET_PLUS_PAGE = 103; - private static final int OFFSET_VAL_PLUS_ONE = 101; - private static final int OFFSET_STRING_PLUS_PAGE = 105; + private static final int OFFSET_PLUS_PAGE = INITIAL_OFFSET + PAGE_SIZE; + private static final int OFFSET_VAL = 1; + private static final int OFFSET_VAL_PLUS_ONE = OFFSET_VAL + PAGE_SIZE; + private static final int OFFSET_STRING = 5; + private static final int OFFSET_STRING_PLUS_PAGE = OFFSET_STRING + PAGE_SIZE; private static final int NUMERIC_OFFSET = 5; + private static final int INVALID_OFFSET_RESULT = -1; + private static final String INVALID_OFFSET_STRING = "5a"; /** * JUnit rule to initialize Mockito annotations. @@ -36,7 +54,7 @@ public class OffsetPaginationTest { public MockitoRule initRule = MockitoJUnit.rule().silent(); @Test - public void testValidOffsetHeaderReturnsTrue() { + public void testValidOffsetHeader() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -56,10 +74,188 @@ public void testValidOffsetHeaderReturnsTrue() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + + @Test + public void testBodyWithTypeCoreFileWrapper() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + // Initial body to be serialized + CoreFileWrapper initialBody = new CoreFileWrapper(){ + + @Override + public File getFile() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getContentType() { + // TODO Auto-generated method stub + return null; + }}; + // Build request with bodySerializer instead of bodyParam + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(initialBody)); + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // OffsetPagination that will hit the bodySerializer branch (point == "") + OffsetPagination offset = new OffsetPagination("$request.body"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNull(requestBuilder); + } + + @Test + public void testValidOffsetWithBodySerializerIOException() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + // Create a body that is not CoreFileWrapper + Object body = new Object(); + + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(body)) + .bodySerializer(() -> { + throw new IOException("Simulated IOException"); + }); + + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + + // Use the pointer that will hit the bodySerializer branch + OffsetPagination offset = new OffsetPagination("$request.body"); + + // The test passes if no exception is thrown and method completes + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + + assertNull(requestBuilder); + } + + + @Test + public void testValidOffsetWithBodySerializer() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + // Initial body to be serialized + String initialBody = INITIAL_OFFSET_STRING; + // Build request with bodySerializer instead of bodyParam + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(INITIAL_OFFSET)) + .bodySerializer(() -> initialBody); + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // OffsetPagination that will hit the bodySerializer branch (point == "") + OffsetPagination offset = new OffsetPagination("$request.body"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + // Verify metadata update correctly + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + + @Test + public void testValidOffsetSingleBodyParamIntType() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(INITIAL_OFFSET)); + + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + + OffsetPagination offset = new OffsetPagination("$request.body"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + // Verify metadata update correctly + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + @Test - public void testValidOffsetTemplateReturnsTrue() { + public void testValidOffsetSingleBodyParamStringType() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(INITIAL_OFFSET_STRING)); + + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + + OffsetPagination offset = new OffsetPagination("$request.body"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + // Verify metadata update correctly + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + + @Test + public void testValidOffsetSingleBodyParam() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + Map body = new HashMap<>(); + body.put("offset", INITIAL_OFFSET); + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.value(body)); + + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + + OffsetPagination offset = new OffsetPagination("$request.body#/offset"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + // Verify metadata update correctly + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + + + @Test + public void testValidOffsetMultipleBodyParams() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + // Test body parameters update + HttpRequest.Builder builder = new HttpRequest.Builder() + .bodyParam(b -> b.key("offset").value(INITIAL_OFFSET)); + + when(paginatedData.getRequestBuilder()).thenReturn(builder); + when(paginatedData.getResponse()).thenReturn(response); + when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + + OffsetPagination offset = new OffsetPagination("$request.body#/offset"); + HttpRequest.Builder requestBuilder = offset.apply(paginatedData); + assertNotNull(requestBuilder); + + // Verify metadata is added correctly + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); + } + + @Test + public void testValidOffsetTemplate() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -67,7 +263,6 @@ public void testValidOffsetTemplateReturnsTrue() { .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("offset").value(INITIAL_OFFSET))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.path#/offset"); @@ -79,10 +274,13 @@ public void testValidOffsetTemplateReturnsTrue() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @Test - public void testValidOffsetReturnsTrue() { + public void testValidOffset() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -90,7 +288,6 @@ public void testValidOffsetReturnsTrue() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("offset").value(INITIAL_OFFSET))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -102,10 +299,13 @@ public void testValidOffsetReturnsTrue() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @Test - public void testValidOffsetAsInnerFieldReturnsTrue() { + public void testValidOffsetAsInnerField() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -114,11 +314,10 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { new HashMap() { private static final long serialVersionUID = 1L; { - put("val", "1"); + put("val", String.valueOf(OFFSET_VAL)); } }))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"offset\": {\"val\": 1}}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset/val"); @@ -130,18 +329,20 @@ public void testValidOffsetAsInnerFieldReturnsTrue() { assertEquals(OFFSET_VAL_PLUS_ONE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_VAL_PLUS_ONE, pageWrapper.getOffsetInput()); } @Test - public void testValidStringOffsetReturnsTrue() { + public void testValidStringOffset() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value("5"))); + q -> q.key("offset").value(String.valueOf(OFFSET_STRING)))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"offset\": \"5\"}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -153,16 +354,19 @@ public void testValidStringOffsetReturnsTrue() { assertEquals(OFFSET_STRING_PLUS_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(OFFSET_STRING_PLUS_PAGE, pageWrapper.getOffsetInput()); } @Test - public void testInvalidStringOffsetReturnsFalse() { + public void testInvalidStringOffset() { PaginatedData paginatedData = mock(PaginatedData.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("offset").value("5a"))); + q -> q.key("offset").value(INVALID_OFFSET_STRING))); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.query#/offset"); @@ -171,13 +375,16 @@ public void testInvalidStringOffsetReturnsFalse() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals("5a", v); + assertEquals(INVALID_OFFSET_STRING, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); + offset.addMetaData(pageWrapper); + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); // Assuming invalid offset sets to default -1 } @Test - public void testMissingOffsetReturnsFalse() { + public void testMissingOffset() { PaginatedData paginatedData = mock(PaginatedData.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); @@ -187,11 +394,15 @@ public void testMissingOffsetReturnsFalse() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); + offset.addMetaData(pageWrapper); + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @Test - public void testNullOffsetReturnsFalse() { + public void testNullOffset() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( @@ -207,5 +418,8 @@ public void testNullOffsetReturnsFalse() { assertEquals(NUMERIC_OFFSET, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + offset.addMetaData(pageWrapper); + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } -} \ No newline at end of file +} diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index b41c7bba..24a3419e 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -16,6 +16,7 @@ import io.apimatic.core.HttpRequest; import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.types.pagination.PagePagination; +import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.response.Response; @@ -24,6 +25,15 @@ */ public class PagePaginationTest { + private static final int INITIAL_PAGE = 3; + private static final int NEXT_PAGE = 4; + private static final int INNER_FIELD_PAGE = 1; + private static final int INNER_FIELD_NEXT_PAGE = 2; + private static final String CURRENT_PAGE_STRING = "5"; + private static final int NEXT_PAGE_FROM_STRING = 6; + private static final String INVALID_PAGE_STRING = "5a"; + private static final int CURRENT_PAGE_INT = 5; + /** * Mockito rule for initializing mocks. */ @@ -31,17 +41,15 @@ public class PagePaginationTest { public MockitoRule initRule = MockitoJUnit.rule().silent(); @Test - public void testWithValidPageHeaderReturnsTrue() { + public void testWithValidPageHeader() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - final int initialPage = 3; - final int nextPage = 4; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key("page").value(initialPage))); + h -> h.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); PagePagination page = new PagePagination("$request.headers#/page"); @@ -49,23 +57,24 @@ public void testWithValidPageHeaderReturnsTrue() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.headers#/page", v -> { - assertEquals(nextPage, v); + assertEquals(NEXT_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + page.addMetaData(pageWrapper); + assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @Test - public void testWithValidPageTemplateReturnsTrue() { + public void testWithValidPageTemplate() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - final int initialPage = 3; - final int nextPage = 4; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().templateParam( - t -> t.key("page").value(initialPage))); + t -> t.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); PagePagination page = new PagePagination("$request.path#/page"); @@ -73,23 +82,24 @@ public void testWithValidPageTemplateReturnsTrue() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.path#/page", v -> { - assertEquals(nextPage, v); + assertEquals(NEXT_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + page.addMetaData(pageWrapper); + assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @Test - public void testWithValidPageReturnsTrue() { + public void testWithValidPage() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - final int initialPage = 3; - final int nextPage = 4; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("page").value(initialPage))); + q -> q.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + initialPage + "}"); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); PagePagination page = new PagePagination("$request.query#/page"); @@ -97,13 +107,16 @@ public void testWithValidPageReturnsTrue() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals(nextPage, v); + assertEquals(NEXT_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + page.addMetaData(pageWrapper); + assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @Test - public void testWithValidPageAsInnerFieldReturnsTrue() { + public void testWithValidPageAsInnerField() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -112,11 +125,11 @@ public void testWithValidPageAsInnerFieldReturnsTrue() { .value(new HashMap() { private static final long serialVersionUID = 1L; { - put("val", "1"); + put("val", String.valueOf(INNER_FIELD_PAGE)); } }))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": {\"val\": 1}}"); + when(response.getBody()).thenReturn("{\"page\": {\"val\": " + INNER_FIELD_PAGE + "}}"); PagePagination page = new PagePagination("$request.query#/page/val"); @@ -124,23 +137,25 @@ public void testWithValidPageAsInnerFieldReturnsTrue() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.query#/page/val", v -> { - assertEquals(2, v); + assertEquals(INNER_FIELD_NEXT_PAGE, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + page.addMetaData(pageWrapper); + + assertEquals(INNER_FIELD_NEXT_PAGE, pageWrapper.getPageInput()); } @Test - public void testWithValidStringPageReturnsTrue() { + public void testWithValidStringPage() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - final String current = "5"; - final int next = 6; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("page").value(current))); + q -> q.key("page").value(CURRENT_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": \"" + current + "\"}"); + when(response.getBody()).thenReturn("{\"page\": \"" + CURRENT_PAGE_STRING + "\"}"); PagePagination page = new PagePagination("$request.query#/page"); @@ -148,20 +163,24 @@ public void testWithValidStringPageReturnsTrue() { assertNotNull(requestBuilder); requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals(next, v); + assertEquals(NEXT_PAGE_FROM_STRING, v); return v; }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + page.addMetaData(pageWrapper); + + assertEquals(NEXT_PAGE_FROM_STRING, pageWrapper.getPageInput()); } @Test - public void testWithInvalidStringPageReturnsFalse() { + public void testWithInvalidStringPage() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value("5a"))); + .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value(INVALID_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": \"5a\"}"); + when(response.getBody()).thenReturn("{\"page\": \"" + INVALID_PAGE_STRING + "\"}"); PagePagination page = new PagePagination("$request.query#/page"); @@ -170,7 +189,7 @@ public void testWithInvalidStringPageReturnsFalse() { } @Test - public void testWithMissingPageReturnsFalse() { + public void testWithMissingPage() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); @@ -185,20 +204,19 @@ public void testWithMissingPageReturnsFalse() { } @Test - public void testWithNullPageReturnsFalse() { + public void testWithNullPage() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - final int current = 5; when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key("page").value(current))); + q -> q.key("page").value(CURRENT_PAGE_INT))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + current + "}"); + when(response.getBody()).thenReturn("{\"page\": " + CURRENT_PAGE_INT + "}"); PagePagination page = new PagePagination(null); Builder requestBuilder = page.apply(paginatedData); assertNull(requestBuilder); } -} \ No newline at end of file +} From fce74d65d301439ebeaabf72982665ca85aa1d85 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Wed, 11 Jun 2025 14:23:13 +0500 Subject: [PATCH 12/33] refactored pagination test --- .../type/pagination/CursorPaginationTest.java | 17 ------- .../type/pagination/LinkPaginationTest.java | 50 ------------------- .../type/pagination/OffsetPaginationTest.java | 48 +----------------- .../type/pagination/PagePaginationTest.java | 36 ------------- 4 files changed, 2 insertions(+), 149 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 101d7202..f3cdccb0 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -30,7 +30,6 @@ public class CursorPaginationTest { @Rule public MockitoRule initRule = MockitoJUnit.rule().silent(); - // Constants private static final String CURSOR_KEY = "cursor"; private static final String RESPONSE_POINTER_VALID = "$response.body#/next_cursor"; private static final String RESPONSE_POINTER_INVALID = "$response.body#/next"; @@ -59,10 +58,6 @@ public void testWithValidCursor() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { - assertEquals("xyz123", v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("xyz123", pageWrapper.getCursorInput()); @@ -87,10 +82,6 @@ public void testWithValidCursorAndDifferentTypeA() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { - assertEquals("123", v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); @@ -116,10 +107,6 @@ public void testWithValidCursorAndDifferentType() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { - assertEquals("123", v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); @@ -162,10 +149,6 @@ public void testWithValidCursorFromResponseBody() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { - assertEquals("123", v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index f18e225e..5e769fe7 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -61,11 +61,6 @@ public void testValidLink() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { - assertEquals("2", v); - return v; - }); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); @@ -90,21 +85,6 @@ public void testValidLinkWithAdditionalParams() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { - assertEquals("2", v); - return v; - }); - - requestBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { - assertEquals(pageSize, v); - return v; - }); - - requestBuilder.updateByReference(REQUEST_HEADERS_PAGE, v -> { - assertEquals(2, v); - return v; - }); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); @@ -125,10 +105,6 @@ public void testValidLinkFromHeader() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { - assertEquals("2", v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); @@ -294,8 +270,6 @@ public void testInvalidPointer() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); - LinkPagination link = new LinkPagination("$response.body#/next/href"); assertNull(link.apply(paginatedData)); @@ -308,8 +282,6 @@ public void testMissingResponse() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn(null); - LinkPagination link = new LinkPagination("$response.body#/next/href"); assertNull(link.apply(paginatedData)); @@ -322,8 +294,6 @@ public void testMissingPointer() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); - LinkPagination link = new LinkPagination(null); assertNull(link.apply(paginatedData)); @@ -344,16 +314,6 @@ public void testMultipleQueryParams() { Builder nextBuilder = link.apply(paginatedData); assertNotNull(nextBuilder); - nextBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { - assertEquals("2", v); - return v; - }); - - nextBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { - assertEquals("5", v); - return v; - }); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_MULTIPLE, pageWrapper.getNextLinkInput()); @@ -374,16 +334,6 @@ public void testEncodedQueryParams() { Builder builder = link.apply(paginatedData); assertNotNull(builder); - builder.updateByReference("$request.query#/page o", v -> { - assertEquals("2 a", v); - return v; - }); - - builder.updateByReference("$request.query#/size q", v -> { - assertEquals("5^!4$#", v); - return v; - }); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_ENCODED, pageWrapper.getNextLinkInput()); diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 606b55cb..7a00a028 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -62,7 +62,6 @@ public void testValidOffsetHeader() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("offset").value(INITIAL_OFFSET))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); @@ -70,10 +69,6 @@ public void testValidOffsetHeader() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.headers#/offset", v -> { - assertEquals(OFFSET_PLUS_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -83,7 +78,6 @@ public void testValidOffsetHeader() { public void testBodyWithTypeCoreFileWrapper() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - // Initial body to be serialized CoreFileWrapper initialBody = new CoreFileWrapper(){ @Override @@ -97,13 +91,11 @@ public String getContentType() { // TODO Auto-generated method stub return null; }}; - // Build request with bodySerializer instead of bodyParam HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(initialBody)); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // OffsetPagination that will hit the bodySerializer branch (point == "") OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNull(requestBuilder); @@ -116,7 +108,6 @@ public void testValidOffsetWithBodySerializerIOException() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - // Create a body that is not CoreFileWrapper Object body = new Object(); HttpRequest.Builder builder = new HttpRequest.Builder() @@ -129,10 +120,8 @@ public void testValidOffsetWithBodySerializerIOException() { when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // Use the pointer that will hit the bodySerializer branch OffsetPagination offset = new OffsetPagination("$request.body"); - // The test passes if no exception is thrown and method completes HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNull(requestBuilder); @@ -143,20 +132,16 @@ public void testValidOffsetWithBodySerializerIOException() { public void testValidOffsetWithBodySerializer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - // Initial body to be serialized String initialBody = INITIAL_OFFSET_STRING; - // Build request with bodySerializer instead of bodyParam HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(INITIAL_OFFSET)) .bodySerializer(() -> initialBody); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // OffsetPagination that will hit the bodySerializer branch (point == "") OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -178,7 +163,6 @@ public void testValidOffsetSingleBodyParamIntType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -200,7 +184,6 @@ public void testValidOffsetSingleBodyParamStringType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -224,7 +207,6 @@ public void testValidOffsetSingleBodyParam() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -236,7 +218,6 @@ public void testValidOffsetMultipleBodyParams() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - // Test body parameters update HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.key("offset").value(INITIAL_OFFSET)); @@ -248,7 +229,6 @@ public void testValidOffsetMultipleBodyParams() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - // Verify metadata is added correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -270,10 +250,6 @@ public void testValidOffsetTemplate() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.path#/offset", v -> { - assertEquals(OFFSET_PLUS_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -294,11 +270,7 @@ public void testValidOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - - requestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals(OFFSET_PLUS_PAGE, v); - return v; - }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -325,10 +297,6 @@ public void testValidOffsetAsInnerField() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset/val", v -> { - assertEquals(OFFSET_VAL_PLUS_ONE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_VAL_PLUS_ONE, pageWrapper.getOffsetInput()); @@ -350,10 +318,6 @@ public void testValidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals(OFFSET_STRING_PLUS_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_STRING_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -374,13 +338,9 @@ public void testInvalidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals(INVALID_OFFSET_STRING, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); - assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); // Assuming invalid offset sets to default -1 + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @Test @@ -414,10 +374,6 @@ public void testNullOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { - assertEquals(NUMERIC_OFFSET, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 24a3419e..23b6d1f3 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -49,17 +49,11 @@ public void testWithValidPageHeader() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); - PagePagination page = new PagePagination("$request.headers#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.headers#/page", v -> { - assertEquals(NEXT_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -74,17 +68,11 @@ public void testWithValidPageTemplate() { .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); - PagePagination page = new PagePagination("$request.path#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.path#/page", v -> { - assertEquals(NEXT_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -99,17 +87,11 @@ public void testWithValidPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); - PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals(NEXT_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -129,17 +111,11 @@ public void testWithValidPageAsInnerField() { } }))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": {\"val\": " + INNER_FIELD_PAGE + "}}"); - PagePagination page = new PagePagination("$request.query#/page/val"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page/val", v -> { - assertEquals(INNER_FIELD_NEXT_PAGE, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); @@ -155,17 +131,11 @@ public void testWithValidStringPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(CURRENT_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": \"" + CURRENT_PAGE_STRING + "\"}"); - PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { - assertEquals(NEXT_PAGE_FROM_STRING, v); - return v; - }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); @@ -180,8 +150,6 @@ public void testWithInvalidStringPage() { when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value(INVALID_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": \"" + INVALID_PAGE_STRING + "\"}"); - PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); @@ -195,8 +163,6 @@ public void testWithMissingPage() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{}"); - PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); @@ -212,8 +178,6 @@ public void testWithNullPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(CURRENT_PAGE_INT))); when(paginatedData.getResponse()).thenReturn(response); - when(response.getBody()).thenReturn("{\"page\": " + CURRENT_PAGE_INT + "}"); - PagePagination page = new PagePagination(null); Builder requestBuilder = page.apply(paginatedData); From 3961ed117ce4965a44c981441f295fcd36e0b0f2 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Wed, 11 Jun 2025 14:39:38 +0500 Subject: [PATCH 13/33] rename methods --- src/main/java/io/apimatic/core/HttpRequest.java | 6 +++--- .../core/types/pagination/CursorPagination.java | 14 ++++++++------ .../core/types/pagination/OffsetPagination.java | 14 ++++++++------ .../core/types/pagination/PagePagination.java | 14 ++++++++------ .../core/types/pagination/PaginatedData.java | 10 ++++++---- .../core/type/pagination/OffsetPaginationTest.java | 7 ------- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 26698796..13bfd83b 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -325,13 +325,13 @@ public static class Builder { private Parameter.Builder parameterBuilder = new Parameter.Builder(); /** - * Update the request parameters using a setter. + * Update the request parameters using a setter thats called via a JSON pointer. * * @param pointer A JSON pointer pointing to any request field. * @param setter A function that takes in an old value and returns a new value. * @return The updated instance of current request builder. */ - public Builder updateByReference(String pointer, UnaryOperator setter) { + public Builder updateParameterByJsonPointer(String pointer, UnaryOperator setter) { if (pointer == null) { return this; } @@ -369,7 +369,7 @@ private void updateBodyParams(UnaryOperator setter, String point) { String serializedBody = bodySerializer.supply(); String newSerializedBody = setter.apply(serializedBody).toString(); bodySerializer = () -> newSerializedBody; - } catch (IOException e) { + } catch (Exception e) { // Empty block } return; diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index 3872d457..d026ca7f 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -1,5 +1,7 @@ package io.apimatic.core.types.pagination; +import java.util.concurrent.atomic.AtomicBoolean; + import io.apimatic.core.HttpRequest.Builder; import io.apimatic.core.utilities.CoreHelper; import io.apimatic.coreinterfaces.http.response.Response; @@ -22,13 +24,13 @@ public CursorPagination(final String output, final String input) { public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); - final boolean[] isUpdated = {false}; + AtomicBoolean isUpdated = new AtomicBoolean(false); - reqBuilder.updateByReference(input, old -> { + reqBuilder.updateParameterByJsonPointer(input, old -> { if (response == null) { currentRequestCursor = (String) old; - isUpdated[0] = true; + isUpdated.set(true); return old; } @@ -39,15 +41,15 @@ public Builder apply(PaginatedData paginatedData) { } currentRequestCursor = cursorValue; - isUpdated[0] = true; + isUpdated.set(true); return cursorValue; }); - if (!isUpdated[0] && response == null) { + if (!isUpdated.get() && response == null) { return reqBuilder; } - return isUpdated[0] ? reqBuilder : null; + return isUpdated.get() ? reqBuilder : null; } @Override diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index 2bee47f4..db573351 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -1,5 +1,7 @@ package io.apimatic.core.types.pagination; +import java.util.concurrent.atomic.AtomicBoolean; + import io.apimatic.core.HttpRequest.Builder; import io.apimatic.coreinterfaces.http.response.Response; @@ -18,28 +20,28 @@ public OffsetPagination(final String input) { public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); - final boolean[] isUpdated = {false}; + AtomicBoolean isUpdated = new AtomicBoolean(false); - reqBuilder.updateByReference(input, old -> { + reqBuilder.updateParameterByJsonPointer(input, old -> { int oldValue = Integer.parseInt("" + old); if (response == null) { currentRequestOffset = oldValue; - isUpdated[0] = true; + isUpdated.set(true); return old; } int newValue = oldValue + paginatedData.getPageSize(); currentRequestOffset = newValue; - isUpdated[0] = true; + isUpdated.set(true); return newValue; }); - if (!isUpdated[0] && response == null) { + if (!isUpdated.get() && response == null) { return reqBuilder; } - return isUpdated[0] ? reqBuilder : null; + return isUpdated.get() ? reqBuilder : null; } @Override diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index c9f07ad8..8c190c03 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -1,5 +1,7 @@ package io.apimatic.core.types.pagination; +import java.util.concurrent.atomic.AtomicBoolean; + import io.apimatic.core.HttpRequest.Builder; import io.apimatic.coreinterfaces.http.response.Response; @@ -18,28 +20,28 @@ public PagePagination(final String input) { public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); - final boolean[] isUpdated = {false}; + AtomicBoolean isUpdated = new AtomicBoolean(false); - reqBuilder.updateByReference(input, old -> { + reqBuilder.updateParameterByJsonPointer(input, old -> { int oldValue = Integer.parseInt("" + old); if (response == null) { currentRequestPageNumber = oldValue; - isUpdated[0] = true; + isUpdated.set(true); return old; } int newValue = oldValue + 1; currentRequestPageNumber = newValue; - isUpdated[0] = true; + isUpdated.set(true); return newValue; }); - if (!isUpdated[0] && response == null) { + if (!isUpdated.get() && response == null) { return reqBuilder; } - return isUpdated[0] ? reqBuilder : null; + return isUpdated.get() ? reqBuilder : null; } @Override diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index f853672d..f5817e27 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -79,8 +79,7 @@ public T getPage(Function, T> pageSupplier * @return An Iterator of items of type T */ public Iterator items(Function, T> itemSupplier) { - PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, pageCreator, itemsCreator, strategies); + PaginatedData paginatedData = copy(); return new Iterator() { @Override @@ -107,8 +106,7 @@ public T next() { * @return An Iterator of pages of type T */ public Iterator pages(Function, T> pageSupplier) { - PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, pageCreator, itemsCreator, strategies); + PaginatedData paginatedData = copy(); return new Iterator() { @Override @@ -132,6 +130,10 @@ public T next() { } }; } + + public PaginatedData copy() { + return new PaginatedData<>(firstApiCall, pageCreator, itemsCreator, strategies); + } public CompletableFuture fetchNextPageAsync() { if (dataClosed) { diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 7a00a028..394f34c8 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -3,28 +3,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; -import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; -import java.util.function.UnaryOperator; import org.junit.Rule; import org.junit.Test; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import io.apimatic.core.GlobalConfiguration; import io.apimatic.core.HttpRequest; import io.apimatic.core.HttpRequest.Builder; -import io.apimatic.core.types.OptionalNullable.Serializer; import io.apimatic.core.types.pagination.OffsetPagination; import io.apimatic.core.types.pagination.PageWrapper; import io.apimatic.core.types.pagination.PaginatedData; From 67a7ea7a30a9bcd5f8e71866eabcaed28409e422 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Wed, 11 Jun 2025 14:42:12 +0500 Subject: [PATCH 14/33] Revert "refactored pagination test" This reverts commit fce74d65d301439ebeaabf72982665ca85aa1d85. --- .../type/pagination/CursorPaginationTest.java | 17 +++++++ .../type/pagination/LinkPaginationTest.java | 50 +++++++++++++++++++ .../type/pagination/OffsetPaginationTest.java | 48 +++++++++++++++++- .../type/pagination/PagePaginationTest.java | 36 +++++++++++++ 4 files changed, 149 insertions(+), 2 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index f3cdccb0..101d7202 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -30,6 +30,7 @@ public class CursorPaginationTest { @Rule public MockitoRule initRule = MockitoJUnit.rule().silent(); + // Constants private static final String CURSOR_KEY = "cursor"; private static final String RESPONSE_POINTER_VALID = "$response.body#/next_cursor"; private static final String RESPONSE_POINTER_INVALID = "$response.body#/next"; @@ -58,6 +59,10 @@ public void testWithValidCursor() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + assertEquals("xyz123", v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("xyz123", pageWrapper.getCursorInput()); @@ -82,6 +87,10 @@ public void testWithValidCursorAndDifferentTypeA() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + assertEquals("123", v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); @@ -107,6 +116,10 @@ public void testWithValidCursorAndDifferentType() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + assertEquals("123", v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); @@ -149,6 +162,10 @@ public void testWithValidCursorFromResponseBody() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + assertEquals("123", v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 5e769fe7..f18e225e 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -61,6 +61,11 @@ public void testValidLink() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + assertEquals("2", v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); @@ -85,6 +90,21 @@ public void testValidLinkWithAdditionalParams() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + assertEquals("2", v); + return v; + }); + + requestBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { + assertEquals(pageSize, v); + return v; + }); + + requestBuilder.updateByReference(REQUEST_HEADERS_PAGE, v -> { + assertEquals(2, v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); @@ -105,6 +125,10 @@ public void testValidLinkFromHeader() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + assertEquals("2", v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); @@ -270,6 +294,8 @@ public void testInvalidPointer() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); + LinkPagination link = new LinkPagination("$response.body#/next/href"); assertNull(link.apply(paginatedData)); @@ -282,6 +308,8 @@ public void testMissingResponse() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn(null); + LinkPagination link = new LinkPagination("$response.body#/next/href"); assertNull(link.apply(paginatedData)); @@ -294,6 +322,8 @@ public void testMissingPointer() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"next\": \"" + NEXT_URL_SINGLE + "\"}"); + LinkPagination link = new LinkPagination(null); assertNull(link.apply(paginatedData)); @@ -314,6 +344,16 @@ public void testMultipleQueryParams() { Builder nextBuilder = link.apply(paginatedData); assertNotNull(nextBuilder); + nextBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + assertEquals("2", v); + return v; + }); + + nextBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { + assertEquals("5", v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_MULTIPLE, pageWrapper.getNextLinkInput()); @@ -334,6 +374,16 @@ public void testEncodedQueryParams() { Builder builder = link.apply(paginatedData); assertNotNull(builder); + builder.updateByReference("$request.query#/page o", v -> { + assertEquals("2 a", v); + return v; + }); + + builder.updateByReference("$request.query#/size q", v -> { + assertEquals("5^!4$#", v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.Create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_ENCODED, pageWrapper.getNextLinkInput()); diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 394f34c8..56c3eec0 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -55,6 +55,7 @@ public void testValidOffsetHeader() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("offset").value(INITIAL_OFFSET))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"offset\": " + INITIAL_OFFSET + "}"); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); OffsetPagination offset = new OffsetPagination("$request.headers#/offset"); @@ -62,6 +63,10 @@ public void testValidOffsetHeader() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.headers#/offset", v -> { + assertEquals(OFFSET_PLUS_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -71,6 +76,7 @@ public void testValidOffsetHeader() { public void testBodyWithTypeCoreFileWrapper() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); + // Initial body to be serialized CoreFileWrapper initialBody = new CoreFileWrapper(){ @Override @@ -84,11 +90,13 @@ public String getContentType() { // TODO Auto-generated method stub return null; }}; + // Build request with bodySerializer instead of bodyParam HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(initialBody)); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // OffsetPagination that will hit the bodySerializer branch (point == "") OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNull(requestBuilder); @@ -101,6 +109,7 @@ public void testValidOffsetWithBodySerializerIOException() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); + // Create a body that is not CoreFileWrapper Object body = new Object(); HttpRequest.Builder builder = new HttpRequest.Builder() @@ -113,8 +122,10 @@ public void testValidOffsetWithBodySerializerIOException() { when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // Use the pointer that will hit the bodySerializer branch OffsetPagination offset = new OffsetPagination("$request.body"); + // The test passes if no exception is thrown and method completes HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNull(requestBuilder); @@ -125,16 +136,20 @@ public void testValidOffsetWithBodySerializerIOException() { public void testValidOffsetWithBodySerializer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); + // Initial body to be serialized String initialBody = INITIAL_OFFSET_STRING; + // Build request with bodySerializer instead of bodyParam HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(INITIAL_OFFSET)) .bodySerializer(() -> initialBody); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); + // OffsetPagination that will hit the bodySerializer branch (point == "") OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -156,6 +171,7 @@ public void testValidOffsetSingleBodyParamIntType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -177,6 +193,7 @@ public void testValidOffsetSingleBodyParamStringType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -200,6 +217,7 @@ public void testValidOffsetSingleBodyParam() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + // Verify metadata update correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -211,6 +229,7 @@ public void testValidOffsetMultipleBodyParams() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); + // Test body parameters update HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.key("offset").value(INITIAL_OFFSET)); @@ -222,6 +241,7 @@ public void testValidOffsetMultipleBodyParams() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + // Verify metadata is added correctly PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -243,6 +263,10 @@ public void testValidOffsetTemplate() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.path#/offset", v -> { + assertEquals(OFFSET_PLUS_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -263,7 +287,11 @@ public void testValidOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - + + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals(OFFSET_PLUS_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -290,6 +318,10 @@ public void testValidOffsetAsInnerField() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/offset/val", v -> { + assertEquals(OFFSET_VAL_PLUS_ONE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_VAL_PLUS_ONE, pageWrapper.getOffsetInput()); @@ -311,6 +343,10 @@ public void testValidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals(OFFSET_STRING_PLUS_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_STRING_PLUS_PAGE, pageWrapper.getOffsetInput()); @@ -331,9 +367,13 @@ public void testInvalidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals(INVALID_OFFSET_STRING, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); - assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); // Assuming invalid offset sets to default -1 } @Test @@ -367,6 +407,10 @@ public void testNullOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/offset", v -> { + assertEquals(NUMERIC_OFFSET, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 23b6d1f3..24a3419e 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -49,11 +49,17 @@ public void testWithValidPageHeader() { .thenReturn(new HttpRequest.Builder().headerParam( h -> h.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); + PagePagination page = new PagePagination("$request.headers#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.headers#/page", v -> { + assertEquals(NEXT_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -68,11 +74,17 @@ public void testWithValidPageTemplate() { .thenReturn(new HttpRequest.Builder().templateParam( t -> t.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); + PagePagination page = new PagePagination("$request.path#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.path#/page", v -> { + assertEquals(NEXT_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -87,11 +99,17 @@ public void testWithValidPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(INITIAL_PAGE))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + INITIAL_PAGE + "}"); + PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/page", v -> { + assertEquals(NEXT_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); @@ -111,11 +129,17 @@ public void testWithValidPageAsInnerField() { } }))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": {\"val\": " + INNER_FIELD_PAGE + "}}"); + PagePagination page = new PagePagination("$request.query#/page/val"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/page/val", v -> { + assertEquals(INNER_FIELD_NEXT_PAGE, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); @@ -131,11 +155,17 @@ public void testWithValidStringPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(CURRENT_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": \"" + CURRENT_PAGE_STRING + "\"}"); + PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); + requestBuilder.updateByReference("$request.query#/page", v -> { + assertEquals(NEXT_PAGE_FROM_STRING, v); + return v; + }); PageWrapper pageWrapper = PageWrapper.Create(response, null, null); page.addMetaData(pageWrapper); @@ -150,6 +180,8 @@ public void testWithInvalidStringPage() { when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value(INVALID_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": \"" + INVALID_PAGE_STRING + "\"}"); + PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); @@ -163,6 +195,8 @@ public void testWithMissingPage() { when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{}"); + PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); @@ -178,6 +212,8 @@ public void testWithNullPage() { .thenReturn(new HttpRequest.Builder().queryParam( q -> q.key("page").value(CURRENT_PAGE_INT))); when(paginatedData.getResponse()).thenReturn(response); + when(response.getBody()).thenReturn("{\"page\": " + CURRENT_PAGE_INT + "}"); + PagePagination page = new PagePagination(null); Builder requestBuilder = page.apply(paginatedData); From fb9a5ac90ea125b61426a6638c744b380f814a47 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Wed, 11 Jun 2025 17:11:27 +0500 Subject: [PATCH 15/33] updated tests to complete functionality coverage --- .../apimatic/core/utilities/CoreHelper.java | 9 - .../apimatic/core/RequestBuilderTest.java | 125 ++++++++++++- .../type/pagination/CheckedSupplierTest.java | 8 +- .../type/pagination/CursorPaginationTest.java | 8 +- .../type/pagination/LinkPaginationTest.java | 176 ++---------------- .../type/pagination/OffsetPaginationTest.java | 34 +--- .../type/pagination/PagePaginationTest.java | 10 +- .../type/pagination/PaginatedDataTest.java | 2 +- .../core/utilities/CoreHelperTest.java | 37 ++++ 9 files changed, 197 insertions(+), 212 deletions(-) diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index 66068baf..fd394e13 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -1678,10 +1678,6 @@ private static String getValueFromJson(String pointer, String json) { JsonValue value = jsonPointer.getValue(jsonStructure); - if (value == JsonValue.NULL) { - return null; - } - if (value instanceof JsonString) { return ((JsonString) value).getString(); } @@ -1758,8 +1754,6 @@ private static Object toObject(JsonValue value) { return true; case FALSE: return false; - case NULL: - return null; default: return value.toString(); } @@ -1772,9 +1766,6 @@ private static Object toObject(JsonValue value) { * @return The corresponding JsonValue or null if unsupported. */ private static JsonValue toJsonValue(Object obj) { - if (obj == null) { - return null; - } if (obj instanceof String) { return Json.createValue((String) obj); } diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index edafafa7..1d3e0c00 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import org.junit.Before; @@ -36,6 +37,7 @@ import org.mockito.stubbing.Answer; import apimatic.core.mocks.MockCoreConfig; +import apimatic.core.models.Atom; import apimatic.core.models.Car; import apimatic.core.models.Employee; import apimatic.core.models.Rfc1123Date; @@ -127,6 +129,121 @@ public void setup() throws IOException { setExpectations(); } + @Test + public void testUpdatePathParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .templateParam(param -> param.key("temp").value(false)); + + updateAndVerify(requestBuilder, "$request.path#/temp", false, true); + } + + @Test + public void testUpdatePathParamArray() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .templateParam(param -> param.key("temp") + .value(new String[] { "apple", "banana", "cherry" })); + + updateAndVerify(requestBuilder, "$request.path#/temp/1", "banana", "mango"); + } + + @Test + public void testUpdateSimpleFormParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .formParam(param -> param.key("form").value(true)); + + updateAndVerify(requestBuilder, "$request.body#/form", true, false); + } + + @Test + public void testUpdateComplexFormParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .formParam(param -> param.key("form").value(new Atom(12, 12))); + + updateAndVerify(requestBuilder, "$request.body#/form/NumberOfElectrons", 12, 14); + } + + @Test + public void testUpdateSimpleQueryParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .queryParam(param -> param.key("que").value(234L)); + + updateAndVerify(requestBuilder, "$request.query#/que", 234L, 254L); + } + + @Test + public void testUpdateComplexQueryParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .queryParam(param -> param.key("que").value(new Atom(12, 12))); + + updateAndVerify(requestBuilder, "$request.query#/que/NumberOfElectrons", 12, 14); + } + + @Test + public void testUpdateSimpleHeaderParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .headerParam(param -> param.key("head").value(2.14)); + + updateAndVerify(requestBuilder, "$request.headers#/head", 2.14, 19.95); + } + + @Test + public void testUpdateComplexHeaderParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + .headerParam(param -> param.key("head").value(new Atom(12, 12))); + + updateAndVerify(requestBuilder, "$request.headers#/head/NumberOfElectrons", 12, 14); + } + + @Test + public void testUpdateMultipleBodyParams() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + .bodyParam(param -> param.key("bodyA").value("bodyValue")) + .bodyParam(param -> param.key("bodyB").value("bodyValue")); + + updateAndVerify(requestBuilder, "$request.body#/bodyB", "bodyValue", "ValueB"); + } + + @Test + public void testUpdateComplexBodyParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + .bodyParam(param -> param.value(new Atom(23, 24))); + + updateAndVerify(requestBuilder, "$request.body#/NumberOfElectrons", 23, 24); + } + + @Test + public void testUpdateMultipleComplexBodyParams() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + .bodyParam(param -> param.key("bodyA").value(new Atom(2, 4))) + .bodyParam(param -> param.key("bodyB").value(new Atom(4, 2))); + + updateAndVerify(requestBuilder, "$request.body#/bodyB/NumberOfElectrons", 4, 8); + } + + @Test + public void testUpdateSimpleBodyParam() throws IOException { + HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + .bodyParam(param -> param.value("BodyValue")); + + updateAndVerify(requestBuilder, "$request.body", "BodyValue", "new Value"); + } + + private void updateAndVerify(HttpRequest.Builder requestBuilder, String pointer, Object oldValue, Object newValue) { + requestBuilder.updateParameterByJsonPointer(pointer, old -> + { + assertEquals(oldValue.toString(), old.toString()); + return newValue; + }); + AtomicBoolean asserted = new AtomicBoolean(false); + requestBuilder.updateParameterByJsonPointer(pointer, newV -> + { + assertEquals(newValue.toString(), newV.toString()); + asserted.set(true); + return newV; + }); + assertTrue(asserted.get()); + } + @Test(expected = NullPointerException.class) public void testBodyParamValidation() throws IOException { // when @@ -157,7 +274,7 @@ public void testBodyParam() throws IOException { } @Test - public void testCloneBodyParam() throws IOException { + public void testClonedBodyParam() throws IOException { // when Request coreHttpRequest = new HttpRequest.Builder().httpMethod(Method.PATCH) @@ -170,7 +287,6 @@ public void testCloneBodyParam() throws IOException { assertEquals(coreHttpRequest.getBody(), "bodyValue"); } - @Test public void testBodyParamKey1() throws IOException { // when @@ -734,6 +850,7 @@ public void testHeaderAuthenticationWithNull() throws IOException { when(authentications.get("global")) .thenReturn(new HeaderAuth(Collections.singletonMap(null, null))); + @SuppressWarnings("unused") Request coreHttpRequest = new HttpRequest.Builder().server("https:\\localhost:3000").path("/auth/basic") .formParam(param -> param.key("key").value("string")) @@ -748,6 +865,7 @@ public void testHeaderAuthenticationWithValueNull() throws IOException { when(authentications.get("global")) .thenReturn(new HeaderAuth(Collections.singletonMap("authorization", null))); + @SuppressWarnings("unused") Request coreHttpRequest = new HttpRequest.Builder().server("https:\\localhost:3000").path("/auth/basic") .formParam(param -> param.key("key").value("string")) @@ -784,6 +902,7 @@ public void testQueryAuthenticationWithNull() throws IOException { authParams.put(null, null); authParams.put("api-key", "apikey"); when(authentications.get("global")).thenReturn(new QueryAuth(authParams)); + @SuppressWarnings("unused") Request coreHttpRequest = new HttpRequest.Builder().server("https:\\localhost:3000").path("/auth/basic") .formParam(param -> param.key("key").value("string")) @@ -798,6 +917,7 @@ public void testQueryAuthenticationWithKeyNull() throws IOException { authParams.put(null, "api-token"); authParams.put("api-key", "apikey"); when(authentications.get("global")).thenReturn(new QueryAuth(authParams)); + @SuppressWarnings("unused") Request coreHttpRequest = new HttpRequest.Builder().server("https:\\localhost:3000").path("/auth/basic") .formParam(param -> param.key("key").value("string")) @@ -812,6 +932,7 @@ public void testQueryAuthenticationWithValueNull() throws IOException { authParams.put("token", null); authParams.put("api-key", "apikey"); when(authentications.get("global")).thenReturn(new QueryAuth(authParams)); + @SuppressWarnings("unused") Request coreHttpRequest = new HttpRequest.Builder().server("https:\\localhost:3000").path("/auth/basic") .formParam(param -> param.key("key").value("string")) diff --git a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java index f7589a5a..956515ec 100644 --- a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java +++ b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java @@ -14,20 +14,22 @@ public class CheckedSupplierTest { public void testCreateErrorWithIOException() throws Exception { IOException ioException = new IOException("Test IO Exception"); CheckedSupplier supplier = CheckedSupplier.CreateError(ioException); - supplier.get(); // Should throw IOException + @SuppressWarnings("unused") + String value = supplier.get(); } @Test(expected = CoreApiException.class) public void testCreateErrorWithCoreApiException() throws Exception { CoreApiException apiException = new CoreApiException("Test API Exception"); CheckedSupplier supplier = CheckedSupplier.CreateError(apiException); - supplier.get(); // Should throw CoreApiException + @SuppressWarnings("unused") + String value = supplier.get(); } @Test public void testCreateErrorWithUnsupportedException() { RuntimeException runtimeException = new RuntimeException("Test Exception"); CheckedSupplier supplier = CheckedSupplier.CreateError(runtimeException); - assertNull(supplier); // Should return null + assertNull(supplier); } } diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 101d7202..8c500599 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -59,7 +59,7 @@ public void testWithValidCursor() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_POINTER, v -> { assertEquals("xyz123", v); return v; }); @@ -87,7 +87,7 @@ public void testWithValidCursorAndDifferentTypeA() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); @@ -116,7 +116,7 @@ public void testWithValidCursorAndDifferentType() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); @@ -162,7 +162,7 @@ public void testWithValidCursorFromResponseBody() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_POINTER, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_POINTER, v -> { assertEquals("123", v); return v; }); diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index f18e225e..14626400 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -7,9 +7,7 @@ import static org.mockito.Mockito.when; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Set; import org.junit.Rule; import org.junit.Test; @@ -61,7 +59,7 @@ public void testValidLink() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); @@ -90,17 +88,17 @@ public void testValidLinkWithAdditionalParams() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); - requestBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_SIZE, v -> { assertEquals(pageSize, v); return v; }); - requestBuilder.updateByReference(REQUEST_HEADERS_PAGE, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_HEADERS_PAGE, v -> { assertEquals(2, v); return v; }); @@ -116,16 +114,18 @@ public void testValidLinkFromHeader() { Response response = mock(Response.class); Map headers = new HashMap<>(); headers.put(NEXT, NEXT_URL_SINGLE); + HttpHeaders httpHeaders = mock(HttpHeaders.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getResponse()).thenReturn(response); - when(response.getHeaders()).thenReturn(createHttpHeaders(headers)); + when(httpHeaders.asSimpleMap()).thenReturn(headers); + when(response.getHeaders()).thenReturn(httpHeaders); LinkPagination link = new LinkPagination(RESPONSE_HEADERS_POINTER); Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + requestBuilder.updateParameterByJsonPointer(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); @@ -135,158 +135,6 @@ public void testValidLinkFromHeader() { assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } - private HttpHeaders createHttpHeaders(Map headers) { - - return new HttpHeaders() { - - - - @Override - - public List values(String headerName) { - - // TODO Auto-generated method stub - - return null; - - } - - - - @Override - - public String value(String headerName) { - - // TODO Auto-generated method stub - - return null; - - } - - - - @Override - - public List remove(String headerName) { - - // TODO Auto-generated method stub - - return null; - - } - - - - @Override - - public Set names() { - - // TODO Auto-generated method stub - - return null; - - } - - - - @Override - - public boolean has(String headerName) { - - // TODO Auto-generated method stub - - return false; - - } - - - - @Override - - public Map asSimpleMap() { - - // TODO Auto-generated method stub - - return headers; - - } - - - - @Override - - public Map> asMultimap() { - - // TODO Auto-generated method stub - - return null; - - } - - - - @Override - - public void addAllFromMultiMap(Map> headers) { - - // TODO Auto-generated method stub - - - - } - - - - @Override - - public void addAllFromMap(Map headers) { - - // TODO Auto-generated method stub - - - - } - - - - @Override - - public void addAll(HttpHeaders headers) { - - // TODO Auto-generated method stub - - - - } - - - - @Override - - public void add(String headerName, List values) { - - // TODO Auto-generated method stub - - - - } - - - - @Override - - public void add(String headerName, String value) { - - // TODO Auto-generated method stub - - - - } - - }; - - } - @Test public void testInvalidPointer() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -344,12 +192,12 @@ public void testMultipleQueryParams() { Builder nextBuilder = link.apply(paginatedData); assertNotNull(nextBuilder); - nextBuilder.updateByReference(REQUEST_QUERY_PAGE, v -> { + nextBuilder.updateParameterByJsonPointer(REQUEST_QUERY_PAGE, v -> { assertEquals("2", v); return v; }); - nextBuilder.updateByReference(REQUEST_QUERY_SIZE, v -> { + nextBuilder.updateParameterByJsonPointer(REQUEST_QUERY_SIZE, v -> { assertEquals("5", v); return v; }); @@ -374,12 +222,12 @@ public void testEncodedQueryParams() { Builder builder = link.apply(paginatedData); assertNotNull(builder); - builder.updateByReference("$request.query#/page o", v -> { + builder.updateParameterByJsonPointer("$request.query#/page o", v -> { assertEquals("2 a", v); return v; }); - builder.updateByReference("$request.query#/size q", v -> { + builder.updateParameterByJsonPointer("$request.query#/size q", v -> { assertEquals("5^!4$#", v); return v; }); diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 56c3eec0..5fc56574 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -6,7 +6,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -63,7 +62,7 @@ public void testValidOffsetHeader() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.headers#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.headers#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -76,27 +75,14 @@ public void testValidOffsetHeader() { public void testBodyWithTypeCoreFileWrapper() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - // Initial body to be serialized - CoreFileWrapper initialBody = new CoreFileWrapper(){ - - @Override - public File getFile() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getContentType() { - // TODO Auto-generated method stub - return null; - }}; - // Build request with bodySerializer instead of bodyParam + CoreFileWrapper initialBody = mock(CoreFileWrapper.class); HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(initialBody)); + when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); - // OffsetPagination that will hit the bodySerializer branch (point == "") + OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNull(requestBuilder); @@ -263,7 +249,7 @@ public void testValidOffsetTemplate() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.path#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.path#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -288,7 +274,7 @@ public void testValidOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/offset", v -> { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); @@ -318,7 +304,7 @@ public void testValidOffsetAsInnerField() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset/val", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/offset/val", v -> { assertEquals(OFFSET_VAL_PLUS_ONE, v); return v; }); @@ -343,7 +329,7 @@ public void testValidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/offset", v -> { assertEquals(OFFSET_STRING_PLUS_PAGE, v); return v; }); @@ -367,7 +353,7 @@ public void testInvalidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/offset", v -> { assertEquals(INVALID_OFFSET_STRING, v); return v; }); @@ -407,7 +393,7 @@ public void testNullOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/offset", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/offset", v -> { assertEquals(NUMERIC_OFFSET, v); return v; }); diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 24a3419e..78897782 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -56,7 +56,7 @@ public void testWithValidPageHeader() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.headers#/page", v -> { + requestBuilder.updateParameterByJsonPointer("$request.headers#/page", v -> { assertEquals(NEXT_PAGE, v); return v; }); @@ -81,7 +81,7 @@ public void testWithValidPageTemplate() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.path#/page", v -> { + requestBuilder.updateParameterByJsonPointer("$request.path#/page", v -> { assertEquals(NEXT_PAGE, v); return v; }); @@ -106,7 +106,7 @@ public void testWithValidPage() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/page", v -> { assertEquals(NEXT_PAGE, v); return v; }); @@ -136,7 +136,7 @@ public void testWithValidPageAsInnerField() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page/val", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/page/val", v -> { assertEquals(INNER_FIELD_NEXT_PAGE, v); return v; }); @@ -162,7 +162,7 @@ public void testWithValidStringPage() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - requestBuilder.updateByReference("$request.query#/page", v -> { + requestBuilder.updateParameterByJsonPointer("$request.query#/page", v -> { assertEquals(NEXT_PAGE_FROM_STRING, v); return v; }); diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 5ff51bfc..7f4e5d17 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -258,7 +258,7 @@ public void testMultiPaginationData() throws IOException, CoreApiException { "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/INVALID"), + new LinkPagination("$response.INVALID#/next_link"), // invalid next link pointer so 2nd call will be made using page pagination new PagePagination("$request.body#/limit"))); } diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 44c607d3..e41b8ee3 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -35,6 +35,7 @@ import apimatic.core.constants.DateTimeConstants; import apimatic.core.mocks.TestDateTimeHelper; +import apimatic.core.models.Atom; import apimatic.core.models.AtomCase; import apimatic.core.models.AttributesAndElements; import apimatic.core.models.CarCase; @@ -1829,6 +1830,42 @@ public void testGetQueryParametersFromUrlWithEmptyUrl() { assertEquals(expectedQueryString, result); } + @Test + public void testUpdateValueByPointerWithInvalidInput() { + String actualValue = "a214"; + + String result = CoreHelper.updateValueByPointer(actualValue, "", v -> "n214"); + assertEquals(actualValue, result); // empty pointer + + result = CoreHelper.updateValueByPointer(actualValue, null, v -> "n214"); + assertEquals(actualValue, result); // null pointer + + result = CoreHelper.updateValueByPointer(actualValue, "/alpha/beta", null); + assertEquals(actualValue, result); // null updater + + result = CoreHelper.updateValueByPointer(null, "/alpha/beta", v -> "n214"); + assertEquals(null, result); // null input + + result = CoreHelper.updateValueByPointer(actualValue, "/alpha/beta", v -> "n214"); + assertEquals(actualValue, result); // invalid JSON + } + + @Test + public void testUpdateValueByPointerInvalidCases() { + Map atoms = new HashMap<>(); + atoms.put("atom", new Atom(14, 26)); + atoms.put("nucleas", new Atom(14, null)); + + Map result = CoreHelper.updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); + assertEquals(atoms, result); // update by null not allowed + + result = CoreHelper.updateValueByPointer(atoms, "/nucleas/NumberOfProtons", v -> 26); + assertEquals(atoms, result); // updating null values not allowed + + result = CoreHelper.updateValueByPointer(atoms, "/atom", v -> new Atom(23, 23)); + assertEquals(atoms, result); // update full object not allowed + } + private Map getComplexType() throws IOException { Map complexType = CoreHelper .deserialize("{\"key1\": {\"numberListType\":[555,666,777],\"numberMapType\":" From 85ed36b3f5e418d3a48bd23f50d2e9cdd3544efc Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Wed, 11 Jun 2025 18:00:32 +0500 Subject: [PATCH 16/33] adds narrowing pagination strategy logic --- .../core/types/pagination/PaginatedData.java | 20 +++++++++++++++++-- .../type/pagination/PaginatedDataTest.java | 8 +++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index f5817e27..4cd5f336 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -23,6 +23,8 @@ public class PaginatedData { private int itemIndex = 0; private final List> items = new ArrayList<>(); private CheckedSupplier page = null; + private PaginationStrategy lockedStrategy; + private boolean canLockStrategy = false; private ApiCall apiCall; private boolean dataClosed = false; @@ -140,7 +142,7 @@ public CompletableFuture fetchNextPageAsync() { return CompletableFuture.completedFuture(false); } - for (PaginationStrategy strategy : strategies) { + for (PaginationStrategy strategy : getStrategies()) { HttpRequest.Builder requestBuilder = strategy.apply(this); if (requestBuilder == null) continue; @@ -161,7 +163,7 @@ private boolean fetchNextPage() { return false; } - for (PaginationStrategy strategy : strategies) { + for (PaginationStrategy strategy : getStrategies()) { HttpRequest.Builder requestBuilder = strategy.apply(this); if (requestBuilder == null) { @@ -182,6 +184,14 @@ private boolean fetchNextPage() { return false; } + private PaginationStrategy[] getStrategies() { + if (lockedStrategy == null) { + return strategies; + } + + return new PaginationStrategy[] { lockedStrategy }; + } + private boolean updateWith(ApiCall apiCall, Res pageUnWrapped, PaginationStrategy strategy) { itemIndex = 0; this.items.clear(); @@ -202,6 +212,12 @@ private boolean updateWith(ApiCall apiCall, Res pageUnWrappe this.page = CheckedSupplier.Create(pageCreator.apply(pageWrapper)); itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.Create(i))); + if (canLockStrategy) { + lockedStrategy = strategy; + } else { + canLockStrategy = true; + } + return true; } diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 7f4e5d17..14c35d15 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -287,8 +287,8 @@ public void testMultiPaginationDataAsync() throws IOException, CoreApiException, InterruptedException, ExecutionException { Runnable call1 = () -> when(response.getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" - + "page_info\":\"fruits\"," - + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); + + "page_info\":\"fruits\"}"); + // doesn't have next_link to force making the next calls using page pagination Runnable call2 = () -> when(response.getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," @@ -298,8 +298,7 @@ public void testMultiPaginationDataAsync() PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/INVALID"), - // invalid next link pointer so 2nd call will be made using page pagination + new LinkPagination("$response.body#/next_link"), new PagePagination("$request.body#/limit")); Function, String> itemCreator = cs -> { @@ -321,7 +320,6 @@ public void testMultiPaginationDataAsync() RecordPage expectedPage1 = new RecordPage(); expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); expectedPage1.setPageInfo("fruits"); - expectedPage1.setNextLink("https://localhost:3000/path?page=2"); RecordPage expectedPage2 = new RecordPage(); expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); From b6f45db0429356b7b3c4c489dc8eb00155fbae92 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 12 Jun 2025 08:55:48 +0500 Subject: [PATCH 17/33] updates readme --- README.md | 7 +++++++ .../apimatic/core/types/pagination/PaginationStrategy.java | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e5400676..5ae7e954 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,12 @@ Core lib's Maven group ID is `io.apimatic`, and its artifact ID is `core`. | [`CoreApiException`](./src/main/java/io/apimatic/core/types/CoreApiException.java) | This is the base class for all exceptions that represent an error response from the server | | [`MultipartFileWrapper`](./src/main/java/io/apimatic/core/types/http/request/MultipartFileWrapper.java) | To wrap file and headers to be sent as part of a multipart request | | [`MultipartWrapper`](./src/main/java/io/apimatic/core/types/http/request/MultipartWrapper.java) | To wrap byteArray and headers to be sent as part of a multipart request | +| [`PaginatedData`](./src/main/java/io/apimatic/core/types/pagination/PaginatedData.java) | To provide pagination functionality for both syncrounous and asyncrounous pagination types | +| [`PageWrapper`](./src/main/java/io/apimatic/core/types/pagination/PageWrapper.java) | To wrap a single page along with its items and meta-data in the paginated data | +| [`CursorPagination`](./src/main/java/io/apimatic/core/types/pagination/CursorPagination.java) | Provides cursor based pagination strategy | +| [`LinkPagination`](./src/main/java/io/apimatic/core/types/pagination/LinkPagination.java) | Provides link based pagination strategy | +| [`OffsetPagination`](./src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java) | Provides offset based pagination strategy | +| [`PagePagination`](./src/main/java/io/apimatic/core/types/pagination/PagePagination.java) | Provides page based pagination strategy | | [`CoreHelper`](./src/main/java/io/apimatic/core/utilities/CoreHelper.java) | This is a Helper class with commonly used utilities for the SDK | | [`DateHelper`](./src/main/java/io/apimatic/core/utilities/DateHelper.java) | This is a utility class for LocalDate operations | | [`LocalDateTimeHelper`](./src/main/java/io/apimatic/core/utilities/LocalDateTimeHelper.java) | This is a utility class for LocalDateTime operations | @@ -75,6 +81,7 @@ Core lib's Maven group ID is `io.apimatic`, and its artifact ID is `core`. | [`RequestExecutor`](./src/main/java/io/apimatic/core/request/async/RequestExecutor.java) | A Request executor that executes request and returns the response asynchronously | | [`RequestSupplier`](./src/main/java/io/apimatic/core/request/async/RequestSupplier.java) | A Request Supplier that supplies the request | | [`TypeCombinator`](./src/main/java/io/apimatic/core/annotations/TypeCombinator.java) | This is a container of annotations for oneOf/anyOf cases | +| [`PaginationStrategy`](./src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java) | Provides the functionality to apply pagination parameters and return new request | ## Links diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java index 1adcdfda..794c6526 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginationStrategy.java @@ -5,10 +5,13 @@ public interface PaginationStrategy { /** - * @param paginatedData Data to be checked for validity - * @return True if paginated data is valid to make another request + * @param paginatedData Data to be processed for validity. + * @return HttpRequest.Builder if paginated data is valid to make another request. */ HttpRequest.Builder apply(PaginatedData paginatedData); + /** + * @param page A pageWrapper instance that will be updated with meta data. + */ void addMetaData(PageWrapper page); } From e39684dbd12f079079e9f12b0a25427d6cb9991f Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 12 Jun 2025 09:42:16 +0500 Subject: [PATCH 18/33] fixes sonar cube issues --- src/main/java/io/apimatic/core/ApiCall.java | 11 +++-- .../java/io/apimatic/core/HttpRequest.java | 4 +- .../io/apimatic/core/ResponseHandler.java | 11 ++--- .../types/pagination/CheckedSupplier.java | 8 ++-- .../core/types/pagination/PaginatedData.java | 8 ++-- .../apimatic/core/ResponseHandlerTest.java | 40 +++++++++---------- .../type/pagination/CheckedSupplierTest.java | 6 +-- 7 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/main/java/io/apimatic/core/ApiCall.java b/src/main/java/io/apimatic/core/ApiCall.java index c502b23f..da11e597 100644 --- a/src/main/java/io/apimatic/core/ApiCall.java +++ b/src/main/java/io/apimatic/core/ApiCall.java @@ -105,7 +105,7 @@ public ResponseType execute() throws IOException, ExceptionType { Context context = globalConfig.getCompatibilityFactory() .createHttpContext(request, response); - return responseHandler.handle(context, endpointConfiguration, globalConfig, requestBuilder); + return responseHandler.handle(context, endpointConfiguration, globalConfig); } /** @@ -116,12 +116,11 @@ public CompletableFuture executeAsync() { return AsyncExecutor.makeHttpCallAsync(() -> requestBuilder.build(globalConfig), request -> globalConfig.getHttpClient() .executeAsync(request, endpointConfiguration), - (request, response) -> { - this.response = response; + (req, res) -> { + this.response = res; Context context = globalConfig.getCompatibilityFactory() - .createHttpContext(request, response); - return responseHandler.handle(context, endpointConfiguration, globalConfig, - requestBuilder); + .createHttpContext(req, res); + return responseHandler.handle(context, endpointConfiguration, globalConfig); }, apiLogger); } diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 13bfd83b..3bc49470 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -364,7 +364,7 @@ private void updateBodyParams(UnaryOperator setter, String point) { return; } - if (bodySerializer != null && point == "") { + if (bodySerializer != null && "".equals(point)) { try { String serializedBody = bodySerializer.supply(); String newSerializedBody = setter.apply(serializedBody).toString(); @@ -375,7 +375,7 @@ private void updateBodyParams(UnaryOperator setter, String point) { return; } - if ((body instanceof String && point == "") || point == "") { + if ((body instanceof String && "".equals(point)) || "".equals(point)) { body = setter.apply(body); return; } diff --git a/src/main/java/io/apimatic/core/ResponseHandler.java b/src/main/java/io/apimatic/core/ResponseHandler.java index 5092c5a9..793b5438 100644 --- a/src/main/java/io/apimatic/core/ResponseHandler.java +++ b/src/main/java/io/apimatic/core/ResponseHandler.java @@ -114,7 +114,6 @@ private ResponseHandler(final Map> localErrorCa * @param context Context which is made for endpoint. * @param config All endPoint level configuration. * @param globalConfig All apiCall level configuration. - * @param requestBuilder The requestBuilder that can re create current API Call. * * @return An object of type ResponseType. * @throws IOException Signals that an I/O exception of some sort has @@ -122,8 +121,7 @@ private ResponseHandler(final Map> localErrorCa * @throws ExceptionType Represents error response from the server. */ public ResponseType handle(Context context, EndpointConfiguration config, - GlobalConfiguration globalConfig, HttpRequest.Builder requestBuilder) - throws IOException, ExceptionType { + GlobalConfiguration globalConfig) throws IOException, ExceptionType { if (globalConfig.getHttpCallback() != null) { // invoke the callback after response if its not null globalConfig.getHttpCallback().onAfterResponse(context); @@ -140,8 +138,7 @@ public ResponseType handle(Context context, EndpointConfiguration config, // handle errors defined at the API level validateResponse(context); - Object result = convertResponse(context.getResponse(), requestBuilder, config, - globalConfig); + Object result = convertResponse(context.getResponse(), config, globalConfig); if (responseClassType == ResponseClassType.API_RESPONSE || responseClassType == ResponseClassType.DYNAMIC_API_RESPONSE) { @@ -161,8 +158,8 @@ private ResponseType applyContextInitializer(Context httpContext, return (ResponseType) result; } - private Object convertResponse(Response response, HttpRequest.Builder requestBuilder, - EndpointConfiguration config, GlobalConfiguration globalConfig) throws IOException { + private Object convertResponse(Response response, EndpointConfiguration config, + GlobalConfiguration globalConfig) throws IOException { if (responseClassType == ResponseClassType.DYNAMIC_RESPONSE || responseClassType == ResponseClassType.DYNAMIC_API_RESPONSE) { return createDynamicResponse(response, globalConfig.getCompatibilityFactory()); diff --git a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java index e41b9c39..5a4e3bf6 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java +++ b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java @@ -4,10 +4,10 @@ import io.apimatic.core.types.CoreApiException; -public interface CheckedSupplier { +public interface CheckedSupplier { @SuppressWarnings("unchecked") - public static CheckedSupplier CreateError( + public static CheckedSupplier createError( Throwable exception) { if (exception instanceof IOException) { return new CheckedSupplier() @@ -33,7 +33,7 @@ public T get() throws E, IOException { } - public static CheckedSupplier Create(T item) { + public static CheckedSupplier create(T item) { return new CheckedSupplier() { @Override @@ -43,5 +43,5 @@ public T get() throws E, IOException { }; } - T get() throws ExceptionType, IOException; + T get() throws E, IOException; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 4cd5f336..3946bb9e 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -209,8 +209,8 @@ private boolean updateWith(ApiCall apiCall, Res pageUnWrappe this.apiCall = apiCall; PageWrapper pageWrapper = PageWrapper.Create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); strategy.addMetaData(pageWrapper); - this.page = CheckedSupplier.Create(pageCreator.apply(pageWrapper)); - itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.Create(i))); + this.page = CheckedSupplier.create(pageCreator.apply(pageWrapper)); + itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.create(i))); if (canLockStrategy) { lockedStrategy = strategy; @@ -222,10 +222,10 @@ private boolean updateWith(ApiCall apiCall, Res pageUnWrappe } private boolean updateAsFailed(Throwable exp) { - page = CheckedSupplier.CreateError(exp); + page = CheckedSupplier.createError(exp); itemIndex = 0; items.clear(); - items.add(CheckedSupplier.CreateError(exp)); + items.add(CheckedSupplier.createError(exp)); dataClosed = true; return true; diff --git a/src/test/java/apimatic/core/ResponseHandlerTest.java b/src/test/java/apimatic/core/ResponseHandlerTest.java index d29d2aa8..a3fa7407 100644 --- a/src/test/java/apimatic/core/ResponseHandlerTest.java +++ b/src/test/java/apimatic/core/ResponseHandlerTest.java @@ -177,7 +177,7 @@ public void testDeserializerMethod() throws IOException, CoreApiException { // verify assertEquals(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder), "bodyValue"); + getMockGlobalConfig()), "bodyValue"); } @Test @@ -195,7 +195,7 @@ public void testDeserializerMethodUsingRawBody() throws IOException, CoreApiExce // verify assertEquals(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder), inputStream); + getMockGlobalConfig()), inputStream); } @@ -215,7 +215,7 @@ public void testDeserializerMethodUsingContext() throws IOException, CoreApiExce // verify assertEquals(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder).getContext(), model.getContext()); + getMockGlobalConfig()).getContext(), model.getContext()); } @Test @@ -230,7 +230,7 @@ public void testContextWithoutDeserializer() throws IOException, CoreApiExceptio // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); } @Test @@ -245,7 +245,7 @@ public void testDynamicResponseTypeMethod() throws IOException, CoreApiException // verify assertEquals(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder), dynamicType); + getMockGlobalConfig()), dynamicType); } @Test @@ -263,7 +263,7 @@ public void testApiResponseTypeMethod() throws IOException, CoreApiException { // verify assertEquals( coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder).getResult(), + getMockGlobalConfig()).getResult(), stringApiResponseType.getResult()); } @@ -280,7 +280,7 @@ public void testDynamicApiResponseTypeMethod() throws IOException, CoreApiExcept // verify assertEquals( coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder).getResult(), + getMockGlobalConfig()).getResult(), dynamicApiResponseType.getResult()); } @@ -292,7 +292,7 @@ public void testDefaultTypeMethod() throws IOException, CoreApiException { when(coreHttpResponse.getStatusCode()).thenReturn(CREATED_SUCCESS_CODE); // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); } @Test @@ -307,7 +307,7 @@ public void testNullify404() throws IOException, CoreApiException { // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); } @Test @@ -320,7 +320,7 @@ public void testNullify404False() throws IOException, CoreApiException { CoreApiException apiException = assertThrows(CoreApiException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); String expectedMessage = "Not found"; @@ -342,19 +342,19 @@ public void testNullableResponseType() throws IOException, CoreApiException { // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); when(coreHttpResponse.getBody()).thenReturn(""); // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); when(coreHttpResponse.getBody()).thenReturn(" "); // verify assertNull(coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); } @Test @@ -370,20 +370,20 @@ public void testNullableResponseTypeFalse() throws IOException, CoreApiException // verify assertEquals(Integer.valueOf(body), coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder)); + getMockGlobalConfig())); when(coreHttpResponse.getBody()).thenReturn(""); assertThrows(NumberFormatException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); when(coreHttpResponse.getBody()).thenReturn(" "); assertThrows(NumberFormatException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); } @@ -400,7 +400,7 @@ public void testLocalException() throws IOException, CoreApiException { CoreApiException apiException = assertThrows(CoreApiException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); String expectedMessage = "Forbidden"; @@ -423,7 +423,7 @@ public void testDefaultException() throws IOException, CoreApiException { CoreApiException apiException = assertThrows(CoreApiException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); String expectedMessage = "Invalid response."; @@ -449,7 +449,7 @@ public void testDefaultException1() throws IOException, CoreApiException { CoreApiException apiException = assertThrows(CoreApiException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); String expectedMessage = "Invalid response."; @@ -485,7 +485,7 @@ public void testGlobalException() throws IOException, CoreApiException { GlobalTestException apiException = assertThrows(GlobalTestException.class, () -> { coreResponseHandler.handle(context, endpointSetting, - getMockGlobalConfig(), requestBuilder); + getMockGlobalConfig()); }); String expectedMessage = "Bad Request"; diff --git a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java index 956515ec..2a058d43 100644 --- a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java +++ b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java @@ -13,7 +13,7 @@ public class CheckedSupplierTest { @Test(expected = IOException.class) public void testCreateErrorWithIOException() throws Exception { IOException ioException = new IOException("Test IO Exception"); - CheckedSupplier supplier = CheckedSupplier.CreateError(ioException); + CheckedSupplier supplier = CheckedSupplier.createError(ioException); @SuppressWarnings("unused") String value = supplier.get(); } @@ -21,7 +21,7 @@ public void testCreateErrorWithIOException() throws Exception { @Test(expected = CoreApiException.class) public void testCreateErrorWithCoreApiException() throws Exception { CoreApiException apiException = new CoreApiException("Test API Exception"); - CheckedSupplier supplier = CheckedSupplier.CreateError(apiException); + CheckedSupplier supplier = CheckedSupplier.createError(apiException); @SuppressWarnings("unused") String value = supplier.get(); } @@ -29,7 +29,7 @@ public void testCreateErrorWithCoreApiException() throws Exception { @Test public void testCreateErrorWithUnsupportedException() { RuntimeException runtimeException = new RuntimeException("Test Exception"); - CheckedSupplier supplier = CheckedSupplier.CreateError(runtimeException); + CheckedSupplier supplier = CheckedSupplier.createError(runtimeException); assertNull(supplier); } } From f2d3c04637a4565f66a5440434b163a57ca91a14 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 10:55:31 +0500 Subject: [PATCH 19/33] test(pagination): sonarcube refactor --- .../core/types/pagination/PageWrapper.java | 2 +- .../core/types/pagination/PaginatedData.java | 64 ++++++++++--------- .../apimatic/core/utilities/CoreHelper.java | 3 +- .../apimatic/core/RequestBuilderTest.java | 2 +- .../type/pagination/CursorPaginationTest.java | 8 +-- .../type/pagination/LinkPaginationTest.java | 10 +-- .../type/pagination/OffsetPaginationTest.java | 26 ++++---- .../type/pagination/PagePaginationTest.java | 10 +-- .../type/pagination/PaginatedDataTest.java | 53 +++++++-------- 9 files changed, 91 insertions(+), 87 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index c72a0f16..533d50da 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -8,7 +8,7 @@ public class PageWrapper implements ApiResponseType

{ - public static PageWrapper Create( + public static PageWrapper create( Response response, P page, List items) { return new PageWrapper(response.getStatusCode(), response.getHeaders(), page, items); } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index f853672d..92117807 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -13,22 +13,22 @@ import io.apimatic.core.types.CoreApiException; import io.apimatic.coreinterfaces.http.response.Response; -public class PaginatedData { +public class PaginatedData { - private final ApiCall firstApiCall; - private final Function, P> pageCreator; - private final Function> itemsCreator; + private final ApiCall firstApiCall; + private final Function, P> pageCreator; + private final Function> itemsCreator; private final PaginationStrategy[] strategies; private int itemIndex = 0; - private final List> items = new ArrayList<>(); - private CheckedSupplier page = null; - private ApiCall apiCall; + private final List> items = new ArrayList<>(); + private CheckedSupplier page = null; + private ApiCall apiCall; private boolean dataClosed = false; - public PaginatedData(final ApiCall apiCall, - final Function, P> pageCreator, - final Function> itemsCreator, + public PaginatedData(final ApiCall apiCall, + final Function, P> pageCreator, + final Function> itemsCreator, final PaginationStrategy... strategies) { this.firstApiCall = apiCall; this.pageCreator = pageCreator; @@ -58,16 +58,17 @@ public int getPageSize() { return items.size(); } - public List getItems(Function, T> itemCreator) { - List items = new ArrayList<>(); - for (CheckedSupplier i : this.items) { - items.add(itemCreator.apply(i)); + public List getItems(Function, T> itemCreator) { + List createdItems = new ArrayList<>(); + for (CheckedSupplier i : this.items) { + createdItems.add(itemCreator.apply(i)); } - - return items; + + return createdItems; } - public T getPage(Function, T> pageSupplier) { + + public T getPage(Function, T> pageSupplier) { if (page == null) { return null; } @@ -78,8 +79,8 @@ public T getPage(Function, T> pageSupplier /** * @return An Iterator of items of type T */ - public Iterator items(Function, T> itemSupplier) { - PaginatedData paginatedData = new PaginatedData<>( + public Iterator items(Function, T> itemSupplier) { + PaginatedData paginatedData = new PaginatedData<>( firstApiCall, pageCreator, itemsCreator, strategies); return new Iterator() { @@ -106,8 +107,8 @@ public T next() { /** * @return An Iterator of pages of type T */ - public Iterator pages(Function, T> pageSupplier) { - PaginatedData paginatedData = new PaginatedData<>( + public Iterator pages(Function, T> pageSupplier) { + PaginatedData paginatedData = new PaginatedData<>( firstApiCall, pageCreator, itemsCreator, strategies); return new Iterator() { @@ -126,10 +127,11 @@ public T next() { throw new NoSuchElementException("No more pages available."); } - T page = pageSupplier.apply(paginatedData.page); + T currentPage = pageSupplier.apply(paginatedData.page); paginatedData.page = null; - return page; + return currentPage; } + }; } @@ -142,7 +144,7 @@ public CompletableFuture fetchNextPageAsync() { HttpRequest.Builder requestBuilder = strategy.apply(this); if (requestBuilder == null) continue; - ApiCall newApiCall = this.apiCall.toBuilder() + ApiCall newApiCall = this.apiCall.toBuilder() .requestBuilder(requestBuilder) .build(); @@ -167,11 +169,11 @@ private boolean fetchNextPage() { } try { - ApiCall apiCall = this.apiCall.toBuilder() + ApiCall nextApiCall = this.apiCall.toBuilder() .requestBuilder(requestBuilder).build(); - Res pageUnWrapped = apiCall.execute(); + R pageUnWrapped = nextApiCall.execute(); - return updateWith(apiCall, pageUnWrapped, strategy); + return updateWith(nextApiCall, pageUnWrapped, strategy); } catch (IOException | CoreApiException exp) { return updateAsFailed(exp); } @@ -180,22 +182,22 @@ private boolean fetchNextPage() { return false; } - private boolean updateWith(ApiCall apiCall, Res pageUnWrapped, PaginationStrategy strategy) { + private boolean updateWith(ApiCall apiCall, R pageUnWrapped, PaginationStrategy strategy) { itemIndex = 0; this.items.clear(); this.page = null; - + if (pageUnWrapped == null) { return false; } List itemsUnWrapped = itemsCreator.apply(pageUnWrapped); - if (itemsUnWrapped == null || itemsUnWrapped.size() == 0) { + if (itemsUnWrapped == null || itemsUnWrapped.isEmpty()) { // 🔥 Fixed here return false; } this.apiCall = apiCall; - PageWrapper pageWrapper = PageWrapper.Create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); + PageWrapper pageWrapper = PageWrapper.create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); strategy.addMetaData(pageWrapper); this.page = CheckedSupplier.Create(pageCreator.apply(pageWrapper)); itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.Create(i))); diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index 66068baf..a59819d1 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -1788,8 +1788,9 @@ private static JsonValue toJsonValue(Object obj) { return Json.createValue((Double) obj); } if (obj instanceof Boolean) { - return (Boolean) obj ? JsonValue.TRUE : JsonValue.FALSE; + return Boolean.TRUE.equals(obj) ? JsonValue.TRUE : JsonValue.FALSE; // ✅ Safe & compliant } return null; } + } diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index edafafa7..3b4712f6 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -167,7 +167,7 @@ public void testCloneBodyParam() throws IOException { when(coreHttpRequest.getBody()).thenReturn("bodyValue"); // verify - assertEquals(coreHttpRequest.getBody(), "bodyValue"); + assertEquals("bodyValue", coreHttpRequest.getBody()); } diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index f3cdccb0..51e41b93 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -58,7 +58,7 @@ public void testWithValidCursor() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("xyz123", pageWrapper.getCursorInput()); } @@ -82,7 +82,7 @@ public void testWithValidCursorAndDifferentTypeA() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); } @@ -107,7 +107,7 @@ public void testWithValidCursorAndDifferentType() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); } @@ -149,7 +149,7 @@ public void testWithValidCursorFromResponseBody() { Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); cursor.addMetaData(pageWrapper); assertEquals("123", pageWrapper.getCursorInput()); } diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 5e769fe7..ef96f518 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -61,7 +61,7 @@ public void testValidLink() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -85,7 +85,7 @@ public void testValidLinkWithAdditionalParams() { Builder requestBuilder = link.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -106,7 +106,7 @@ public void testValidLinkFromHeader() { assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -314,7 +314,7 @@ public void testMultipleQueryParams() { Builder nextBuilder = link.apply(paginatedData); assertNotNull(nextBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_MULTIPLE, pageWrapper.getNextLinkInput()); } @@ -334,7 +334,7 @@ public void testEncodedQueryParams() { Builder builder = link.apply(paginatedData); assertNotNull(builder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); link.addMetaData(pageWrapper); assertEquals(NEXT_URL_ENCODED, pageWrapper.getNextLinkInput()); } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 7a00a028..c537e3f4 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -69,7 +69,7 @@ public void testValidOffsetHeader() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -142,7 +142,7 @@ public void testValidOffsetWithBodySerializer() { OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -163,7 +163,7 @@ public void testValidOffsetSingleBodyParamIntType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -184,7 +184,7 @@ public void testValidOffsetSingleBodyParamStringType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -207,7 +207,7 @@ public void testValidOffsetSingleBodyParam() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -229,7 +229,7 @@ public void testValidOffsetMultipleBodyParams() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -250,7 +250,7 @@ public void testValidOffsetTemplate() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -271,7 +271,7 @@ public void testValidOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -297,7 +297,7 @@ public void testValidOffsetAsInnerField() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_VAL_PLUS_ONE, pageWrapper.getOffsetInput()); } @@ -318,7 +318,7 @@ public void testValidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_STRING_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -338,7 +338,7 @@ public void testInvalidStringOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); + PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @@ -354,7 +354,7 @@ public void testMissingOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(mock(Response.class), null, null); + PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @@ -374,7 +374,7 @@ public void testNullOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 23b6d1f3..3af0098f 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -54,7 +54,7 @@ public void testWithValidPageHeader() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -73,7 +73,7 @@ public void testWithValidPageTemplate() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -92,7 +92,7 @@ public void testWithValidPage() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -116,7 +116,7 @@ public void testWithValidPageAsInnerField() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); page.addMetaData(pageWrapper); assertEquals(INNER_FIELD_NEXT_PAGE, pageWrapper.getPageInput()); @@ -136,7 +136,7 @@ public void testWithValidStringPage() { Builder requestBuilder = page.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.Create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); page.addMetaData(pageWrapper); assertEquals(NEXT_PAGE_FROM_STRING, pageWrapper.getPageInput()); diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 5ff51bfc..ef471b8e 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -17,6 +17,7 @@ import java.util.function.Function; import org.junit.Test; +import org.junit.runner.RunWith; import apimatic.core.EndToEndTest; import io.apimatic.core.ApiCall; @@ -37,6 +38,10 @@ import io.apimatic.coreinterfaces.http.request.Request; import io.apimatic.coreinterfaces.http.request.configuration.RetryOption; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) public class PaginatedDataTest extends EndToEndTest { @Test @@ -73,30 +78,27 @@ public void testInvalidPaginationWithFailingResponseAsync() hasNext = paginatedData.fetchNextPageAsync().get(); assertFalse(hasNext); } - - @Test - public void testInvalidPaginationWithEmptyItems() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"); - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/next_link")); - assertFalse(paginatedData.pages(cs -> cs).hasNext()); + @Parameters + public static List invalidPaginationTestCases() { + return Arrays.asList(new Object[][] { + // Test case 1: Empty items + { "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}" }, + // Test case 2: Null items + { "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}" }, + // Test case 3: Null page + { null } + }); } - @Test - public void testInvalidPaginationNullItems() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( - "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"); - PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, - new LinkPagination("$response.body#/next_link")); - assertFalse(paginatedData.pages(cs -> cs).hasNext()); + private final String responseBody; + + public PaginatedDataTest(String responseBody) { + this.responseBody = responseBody; } @Test - public void testInvalidPaginationNullPage() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn(null); + public void testInvalidPagination() throws IOException, CoreApiException { + Runnable call1 = () -> when(response.getBody()).thenReturn(responseBody); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); @@ -284,7 +286,7 @@ public void testInvalidPaginationDataAsync() @Test public void testMultiPaginationDataAsync() - throws IOException, CoreApiException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException { Runnable call1 = () -> when(response.getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," @@ -330,10 +332,10 @@ public void testMultiPaginationDataAsync() PageWrapper page = paginatedData.getPage(pageCreator); assertNull(page); - assertTrue(paginatedData.getItems(itemCreator).size() == 0); + assertEquals(0, paginatedData.getItems(itemCreator).size()); // Changed here boolean hasNext = paginatedData.fetchNextPageAsync().get(); - assertTrue(hasNext); + assertEquals(true, hasNext); // Changed here assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); assertEquals(expectedPage1.getData(), page.getResult().getData()); @@ -343,7 +345,7 @@ public void testMultiPaginationDataAsync() assertEquals(-1, page.getPageInput()); hasNext = paginatedData.fetchNextPageAsync().get(); - assertTrue(hasNext); + assertEquals(true, hasNext); // Changed here assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); assertEquals(expectedPage2.getData(), page.getResult().getData()); @@ -353,10 +355,10 @@ public void testMultiPaginationDataAsync() assertEquals(2, page.getPageInput()); hasNext = paginatedData.fetchNextPageAsync().get(); - assertFalse(hasNext); + assertEquals(false, hasNext); // Changed here page = paginatedData.getPage(pageCreator); assertNull(page); - assertTrue(paginatedData.getItems(itemCreator).size() == 0); + assertEquals(0, paginatedData.getItems(itemCreator).size()); // Changed here } private void verifyData(PaginatedData, @@ -451,7 +453,6 @@ else if (callNumber == 3) { } @Override public void onAfterResponse(Context context) { - // TODO Auto-generated method stub } }; return new ApiCall.Builder() From f09102811fc7026c83aebd2dfbcbd292f0fbc88f Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 11:50:32 +0500 Subject: [PATCH 20/33] test(pagination): sonarcube refactor(14) --- .../apimatic/core/RequestBuilderTest.java | 45 ++++++++++--------- .../type/pagination/PaginatedDataTest.java | 3 +- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index cd2d790c..1d360590 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -131,43 +131,44 @@ public void setup() throws IOException { @Test public void testUpdatePathParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .templateParam(param -> param.key("temp").value(false)); - updateAndVerify(requestBuilder, "$request.path#/temp", false, true); + updateAndVerify(localRequestBuilder, "$request.path#/temp", false, true); } + @Test public void testUpdatePathParamArray() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .templateParam(param -> param.key("temp") .value(new String[] { "apple", "banana", "cherry" })); - updateAndVerify(requestBuilder, "$request.path#/temp/1", "banana", "mango"); + updateAndVerify(localRequestBuilder, "$request.path#/temp/1", "banana", "mango"); } @Test public void testUpdateSimpleFormParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .formParam(param -> param.key("form").value(true)); - updateAndVerify(requestBuilder, "$request.body#/form", true, false); + updateAndVerify(localRequestBuilder, "$request.body#/form", true, false); } @Test public void testUpdateComplexFormParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .formParam(param -> param.key("form").value(new Atom(12, 12))); - updateAndVerify(requestBuilder, "$request.body#/form/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", 12, 14); } @Test public void testUpdateSimpleQueryParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .queryParam(param -> param.key("que").value(234L)); - updateAndVerify(requestBuilder, "$request.query#/que", 234L, 254L); + updateAndVerify(localRequestBuilder, "$request.query#/que", 234L, 254L); } @Test @@ -180,52 +181,52 @@ public void testUpdateComplexQueryParam() throws IOException { @Test public void testUpdateSimpleHeaderParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .headerParam(param -> param.key("head").value(2.14)); - updateAndVerify(requestBuilder, "$request.headers#/head", 2.14, 19.95); + updateAndVerify(localRequestBuilder, "$request.headers#/head", 2.14, 19.95); } @Test public void testUpdateComplexHeaderParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .headerParam(param -> param.key("head").value(new Atom(12, 12))); - updateAndVerify(requestBuilder, "$request.headers#/head/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", 12, 14); } @Test public void testUpdateMultipleBodyParams() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.key("bodyA").value("bodyValue")) .bodyParam(param -> param.key("bodyB").value("bodyValue")); - updateAndVerify(requestBuilder, "$request.body#/bodyB", "bodyValue", "ValueB"); + updateAndVerify(localRequestBuilder, "$request.body#/bodyB", "bodyValue", "ValueB"); } @Test public void testUpdateComplexBodyParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.value(new Atom(23, 24))); - updateAndVerify(requestBuilder, "$request.body#/NumberOfElectrons", 23, 24); + updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", 23, 24); } @Test public void testUpdateMultipleComplexBodyParams() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.key("bodyA").value(new Atom(2, 4))) .bodyParam(param -> param.key("bodyB").value(new Atom(4, 2))); - updateAndVerify(requestBuilder, "$request.body#/bodyB/NumberOfElectrons", 4, 8); + updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", 4, 8); } @Test public void testUpdateSimpleBodyParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.value("BodyValue")); - updateAndVerify(requestBuilder, "$request.body", "BodyValue", "new Value"); + updateAndVerify(localRequestBuilder, "$request.body", "BodyValue", "new Value"); } private void updateAndVerify(HttpRequest.Builder requestBuilder, String pointer, Object oldValue, Object newValue) { diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 23612f87..a92a3de9 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -97,7 +97,7 @@ public PaginatedDataTest(String responseBody) { } @Test - public void testInvalidPagination() throws IOException, CoreApiException { + public void testInvalidPagination() throws IOException { Runnable call1 = () -> when(response.getBody()).thenReturn(responseBody); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, @@ -451,6 +451,7 @@ else if (callNumber == 3) { } @Override public void onAfterResponse(Context context) { + // Does nothing } }; return new ApiCall.Builder() From 6dcaec6d90220fbd6dd21fba904dcfc95e57ca35 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 12 Jun 2025 12:04:28 +0500 Subject: [PATCH 21/33] resolved conflicts --- src/main/java/io/apimatic/core/ApiCall.java | 29 ++++++-- .../java/io/apimatic/core/HttpRequest.java | 40 +++++----- .../io/apimatic/core/ResponseHandler.java | 23 +++--- .../core/authentication/AuthBuilder.java | 5 +- .../types/pagination/CheckedSupplier.java | 36 ++++++--- .../types/pagination/CursorPagination.java | 4 +- .../types/pagination/OffsetPagination.java | 4 +- .../core/types/pagination/PagePagination.java | 2 +- .../core/types/pagination/PageWrapper.java | 17 ++++- .../core/types/pagination/PaginatedData.java | 74 +++++++++++++------ .../type/pagination/OffsetPaginationTest.java | 11 ++- 11 files changed, 161 insertions(+), 84 deletions(-) diff --git a/src/main/java/io/apimatic/core/ApiCall.java b/src/main/java/io/apimatic/core/ApiCall.java index da11e597..0fe40aa8 100644 --- a/src/main/java/io/apimatic/core/ApiCall.java +++ b/src/main/java/io/apimatic/core/ApiCall.java @@ -50,7 +50,7 @@ public final class ApiCall * An instance of {@link ApiLogger} for logging. */ private final ApiLogger apiLogger; - + private Response response; @@ -71,7 +71,18 @@ private ApiCall(final GlobalConfiguration globalConfig, this.endpointConfiguration = endpointConfiguration; this.apiLogger = SdkLoggerFactory.getLogger(globalConfig.getLoggingConfiguration()); } - + + /** + * Prepare this ApiCall for pagination. + * @param Return type for the paginated data. + * @param Type of items in pages. + * @param

Type of pages. + * @param converter Converts the PaginatedData into the instance of type T. + * @param responseToPage Converts the PageWrapper into the instance of type P. + * @param responseToItems Extract list of items of type I from response. + * @param strategies List of applicable pagination strategies. + * @return Converted paginated data into type T + */ public T paginate( Function, T> converter, Function, P> responseToPage, @@ -81,11 +92,11 @@ public T paginate( this, responseToPage, responseToItems, strategies )); } - + public Response getResponse() { return response; } - + public HttpRequest.Builder getRequestBuilder() { return requestBuilder.copy(); } @@ -123,15 +134,19 @@ public CompletableFuture executeAsync() { return responseHandler.handle(context, endpointConfiguration, globalConfig); }, apiLogger); } - + + /** + * Converts this ApiCall instance to its builder. + * @return ApiCall.Builder that can create a copy of this instance. + */ public Builder toBuilder() { Builder builder = new Builder(); - + builder.globalConfig = globalConfig; builder.endpointConfigurationBuilder = endpointConfiguration.toBuilder(); builder.responseHandlerBuilder = responseHandler.toBuilder(); builder.requestBuilder = requestBuilder.copy(); - + return builder; } diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 3bc49470..9f5d88d9 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -365,7 +365,7 @@ private void updateBodyParams(UnaryOperator setter, String point) { } if (bodySerializer != null && "".equals(point)) { - try { + try { String serializedBody = bodySerializer.supply(); String newSerializedBody = setter.apply(serializedBody).toString(); bodySerializer = () -> newSerializedBody; @@ -633,39 +633,45 @@ private static String getSerializedHeaderValue(Object obj) { return CoreHelper.trySerialize(obj); } - + + /** + * @return A query URL combining the path, query and template parameters. + */ public String getQueryUrl() { StringBuilder builder = new StringBuilder(path); CoreHelper.appendUrlWithQueryParameters(builder, queryParams, arraySerializationFormat); CoreHelper.appendUrlWithTemplateParameters(builder, templateParams); - + return builder.toString(); } + /** + * @return A copy of this request builder instance. + */ public Builder copy() { Builder copy = new Builder(); - copy.server = this.server; - copy.path = this.path; - copy.httpMethod = this.httpMethod; - copy.authBuilder = this.authBuilder.copy(); // Ensure AuthBuilder has a copy() method. - copy.queryParams = new HashMap<>(this.queryParams); - for (Map.Entry> entry : this.templateParams.entrySet()) { + copy.server = server; + copy.path = path; + copy.httpMethod = httpMethod; + copy.authBuilder = authBuilder.copy(); + copy.queryParams = new HashMap<>(queryParams); + for (Entry> entry : templateParams.entrySet()) { copy.templateParams.put(entry.getKey(), new SimpleEntry<>(entry.getValue().getKey(), entry.getValue().getValue())); } - for (Map.Entry> entry : this.headerParams.entrySet()) { + for (Entry> entry : headerParams.entrySet()) { copy.headerParams.put(entry.getKey(), new ArrayList<>(entry.getValue())); } - copy.multipartFormParams = new HashSet<>(this.multipartFormParams); - copy.formParamaters = new HashMap<>(this.formParamaters); - copy.body = this.body; - copy.bodySerializer = this.bodySerializer; - if (this.bodyParameters != null) { - copy.bodyParameters = new HashMap<>(this.bodyParameters); + copy.multipartFormParams = new HashSet<>(multipartFormParams); + copy.formParamaters = new HashMap<>(formParamaters); + copy.body = body; + copy.bodySerializer = bodySerializer; + if (bodyParameters != null) { + copy.bodyParameters = new HashMap<>(bodyParameters); } - copy.arraySerializationFormat = this.arraySerializationFormat; + copy.arraySerializationFormat = arraySerializationFormat; copy.parameterBuilder = new Parameter.Builder(); return copy; diff --git a/src/main/java/io/apimatic/core/ResponseHandler.java b/src/main/java/io/apimatic/core/ResponseHandler.java index 793b5438..5e6e86f9 100644 --- a/src/main/java/io/apimatic/core/ResponseHandler.java +++ b/src/main/java/io/apimatic/core/ResponseHandler.java @@ -84,7 +84,6 @@ public final class ResponseHandler> erro } } + /** + * Converts this ResponseHandler to its builder. + * @return ResponseHandler.Builder that can create a copy of this instance. + */ public Builder toBuilder() { Builder builder = new Builder() - .globalErrorCase(this.globalErrorCases != null ? new HashMap<>(this.globalErrorCases) : null) - .deserializer(this.deserializer) - .apiResponseDeserializer(this.intermediateDeserializer) - .responseClassType(this.responseClassType) - .contextInitializer(this.contextInitializer) - .nullify404(this.isNullify404Enabled) - .nullableResponseType(this.isNullableResponseType); - + .globalErrorCase(globalErrorCases != null ? new HashMap<>(globalErrorCases) : null) + .deserializer(deserializer) + .apiResponseDeserializer(intermediateDeserializer) + .responseClassType(responseClassType) + .contextInitializer(contextInitializer) + .nullify404(isNullify404Enabled) + .nullableResponseType(isNullableResponseType); + builder.localErrorCases = localErrorCases; - + return builder; } diff --git a/src/main/java/io/apimatic/core/authentication/AuthBuilder.java b/src/main/java/io/apimatic/core/authentication/AuthBuilder.java index 69cb3b75..f9f6f234 100644 --- a/src/main/java/io/apimatic/core/authentication/AuthBuilder.java +++ b/src/main/java/io/apimatic/core/authentication/AuthBuilder.java @@ -137,6 +137,9 @@ private List buildAuthGroup(Map authMana return auths; } + /** + * @return A copy of this AuthBuilder instance. + */ public AuthBuilder copy() { AuthBuilder copy = new AuthBuilder(); copy.authKeys = new ArrayList<>(this.authKeys); @@ -147,7 +150,7 @@ public AuthBuilder copy() { } copy.authBuilders.put(entry.getKey(), copiedList); } - + return copy; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java index 5a4e3bf6..479a48b1 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java +++ b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java @@ -5,13 +5,18 @@ import io.apimatic.core.types.CoreApiException; public interface CheckedSupplier { - + + /** + * @param Represents type of stored value. + * @param Represents type of stored exception. + * @param value Create an instance with stored exception of type E. + * @return created CheckedSupplier object. + */ @SuppressWarnings("unchecked") - public static CheckedSupplier createError( + static CheckedSupplier createError( Throwable exception) { if (exception instanceof IOException) { - return new CheckedSupplier() - { + return new CheckedSupplier() { @Override public T get() throws E, IOException { throw (IOException) exception; @@ -20,8 +25,7 @@ public T get() throws E, IOException { } if (exception instanceof CoreApiException) { - return new CheckedSupplier() - { + return new CheckedSupplier() { @Override public T get() throws E, IOException { throw (E) exception; @@ -30,18 +34,28 @@ public T get() throws E, IOException { } return null; - } - public static CheckedSupplier create(T item) { - return new CheckedSupplier() - { + /** + * @param Represents type of stored value. + * @param Represents type of stored exception. + * @param value Create an instance with stored value of type T. + * @return created CheckedSupplier object. + */ + static CheckedSupplier create(T value) { + return new CheckedSupplier() { @Override public T get() throws E, IOException { - return item; + return value; } }; } + /** + * Get the stored instance or throw an exception. + * @return The stored instance of type T. + * @throws E + * @throws IOException + */ T get() throws E, IOException; } diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index d026ca7f..8e334af7 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -27,7 +27,7 @@ public Builder apply(PaginatedData paginatedData) { AtomicBoolean isUpdated = new AtomicBoolean(false); reqBuilder.updateParameterByJsonPointer(input, old -> { - + if (response == null) { currentRequestCursor = (String) old; isUpdated.set(true); @@ -44,7 +44,7 @@ public Builder apply(PaginatedData paginatedData) { isUpdated.set(true); return cursorValue; }); - + if (!isUpdated.get() && response == null) { return reqBuilder; } diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index db573351..19de2f17 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -21,7 +21,7 @@ public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); AtomicBoolean isUpdated = new AtomicBoolean(false); - + reqBuilder.updateParameterByJsonPointer(input, old -> { int oldValue = Integer.parseInt("" + old); @@ -36,7 +36,7 @@ public Builder apply(PaginatedData paginatedData) { isUpdated.set(true); return newValue; }); - + if (!isUpdated.get() && response == null) { return reqBuilder; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index 8c190c03..042292a2 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -36,7 +36,7 @@ public Builder apply(PaginatedData paginatedData) { isUpdated.set(true); return newValue; }); - + if (!isUpdated.get() && response == null) { return reqBuilder; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 533d50da..837600c6 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -6,10 +6,18 @@ import io.apimatic.coreinterfaces.http.response.ApiResponseType; import io.apimatic.coreinterfaces.http.response.Response; -public class PageWrapper implements ApiResponseType

{ +public final class PageWrapper implements ApiResponseType

{ - public static PageWrapper create( - Response response, P page, List items) { + /** + * Create an instance of PageWrapper with provided page and meta data. + * @param Represent type of items in the page. + * @param

Represent type of page. + * @param response Response from API call. + * @param page Page to be wrapped. + * @param items Extracted items from the page. + * @return An new instance of PageWrapper. + */ + public static PageWrapper create(Response response, P page, List items) { return new PageWrapper(response.getStatusCode(), response.getHeaders(), page, items); } @@ -23,7 +31,8 @@ public static PageWrapper create( private int pageInput = -1; private String cursorInput = null; - private PageWrapper(int statusCode, HttpHeaders headers, P page, List items) { + private PageWrapper(int statusCode, final HttpHeaders headers, final P page, + final List items) { this.statusCode = statusCode; this.headers = headers; this.page = page; diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 923293e2..8d152160 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -14,12 +14,11 @@ import io.apimatic.coreinterfaces.http.response.Response; public class PaginatedData { - private final ApiCall firstApiCall; private final Function, P> pageCreator; private final Function> itemsCreator; private final PaginationStrategy[] strategies; - + private int itemIndex = 0; private final List> items = new ArrayList<>(); private CheckedSupplier page = null; @@ -28,6 +27,12 @@ public class PaginatedData { private boolean canLockStrategy = false; private boolean dataClosed = false; + /** + * @param apiCall ApiCall instance to be paginated. + * @param pageCreator Converts the PageWrapper into the instance of type P. + * @param itemsCreator Extract list of items of type I from response. + * @param strategies List of applicable pagination strategies. + */ public PaginatedData(final ApiCall apiCall, final Function, P> pageCreator, final Function> itemsCreator, @@ -60,16 +65,27 @@ public int getPageSize() { return items.size(); } + /** + * Get the items in current page converted to type T. + * @param Represents the type of items in list. + * @param pageSupplier A converter to convert the CheckedSupplier of items to type T. + * @return All items in current page converted via itemSupplier. + */ public List getItems(Function, T> itemCreator) { - List createdItems = new ArrayList<>(); + List convertedItems = new ArrayList<>(); for (CheckedSupplier i : this.items) { - createdItems.add(itemCreator.apply(i)); + convertedItems.add(itemCreator.apply(i)); } - return createdItems; + return convertedItems; } - + /** + * Get the current page converted to type T. + * @param Represents the return type. + * @param pageSupplier A converter to convert the CheckedSupplier of page to type T. + * @return Current page converted via pageSupplier. + */ public T getPage(Function, T> pageSupplier) { if (page == null) { return null; @@ -79,9 +95,10 @@ public T getPage(Function, T> pageSupplier) { } /** - * @return An Iterator of items of type T + * @param Represents the type of items in iterator. + * @param itemSupplier A converter to convert the CheckedSupplier of items to type T. + * @return An Iterator of all items of type T. */ - public Iterator items(Function, T> itemSupplier) { PaginatedData paginatedData = new PaginatedData<>( firstApiCall, pageCreator, itemsCreator, strategies); @@ -108,9 +125,10 @@ public T next() { } /** - * @return An Iterator of pages of type T + * @param Represents the type of pages in iterator. + * @param pageSupplier A converter to convert the CheckedSupplier of page to type T. + * @return An Iterator of all pages of type T. */ - public Iterator pages(Function, T> pageSupplier) { PaginatedData paginatedData = new PaginatedData<>( firstApiCall, pageCreator, itemsCreator, strategies); @@ -131,18 +149,25 @@ public T next() { throw new NoSuchElementException("No more pages available."); } - T currentPage = pageSupplier.apply(paginatedData.page); + T convertedPage = pageSupplier.apply(paginatedData.page); paginatedData.page = null; - return currentPage; + return convertedPage; } }; } - + + /** + * @return A copy of this instance of PaginatedData. + */ public PaginatedData copy() { return new PaginatedData<>(firstApiCall, pageCreator, itemsCreator, strategies); } + /** + * Start fetching the next page asynchronously. + * @return A CompletableFuture of boolean instance suggesting if there is a next page or not. + */ public CompletableFuture fetchNextPageAsync() { if (dataClosed) { return CompletableFuture.completedFuture(false); @@ -150,7 +175,9 @@ public CompletableFuture fetchNextPageAsync() { for (PaginationStrategy strategy : getStrategies()) { HttpRequest.Builder requestBuilder = strategy.apply(this); - if (requestBuilder == null) continue; + if (requestBuilder == null) { + continue; + } ApiCall newApiCall = this.apiCall.toBuilder() .requestBuilder(requestBuilder) @@ -177,11 +204,11 @@ private boolean fetchNextPage() { } try { - ApiCall nextApiCall = this.apiCall.toBuilder() + ApiCall newApiCall = apiCall.toBuilder() .requestBuilder(requestBuilder).build(); - R pageUnWrapped = nextApiCall.execute(); + R pageUnWrapped = newApiCall.execute(); - return updateWith(nextApiCall, pageUnWrapped, strategy); + return updateWith(newApiCall, pageUnWrapped, strategy); } catch (IOException | CoreApiException exp) { return updateAsFailed(exp); } @@ -195,11 +222,11 @@ private PaginationStrategy[] getStrategies() { return strategies; } - return new PaginationStrategy[] { lockedStrategy }; + return new PaginationStrategy[] {lockedStrategy}; } - private boolean updateWith(ApiCall apiCall, R pageUnWrapped, PaginationStrategy strategy) { - + private boolean updateWith(ApiCall apiCall, R pageUnWrapped, + PaginationStrategy strategy) { itemIndex = 0; this.items.clear(); this.page = null; @@ -209,12 +236,13 @@ private boolean updateWith(ApiCall apiCall, R pageUnWrapped, PaginationStr } List itemsUnWrapped = itemsCreator.apply(pageUnWrapped); - if (itemsUnWrapped == null || itemsUnWrapped.isEmpty()) { // 🔥 Fixed here + if (itemsUnWrapped == null || itemsUnWrapped.isEmpty()) { return false; } this.apiCall = apiCall; - PageWrapper pageWrapper = PageWrapper.create(apiCall.getResponse(), pageUnWrapped, itemsUnWrapped); + PageWrapper pageWrapper = PageWrapper.create(apiCall.getResponse(), pageUnWrapped, + itemsUnWrapped); strategy.addMetaData(pageWrapper); this.page = CheckedSupplier.create(pageCreator.apply(pageWrapper)); itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.create(i))); @@ -234,7 +262,7 @@ private boolean updateAsFailed(Throwable exp) { items.clear(); items.add(CheckedSupplier.createError(exp)); dataClosed = true; - + return true; } } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index be9fad65..bbbb3872 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -67,7 +67,6 @@ public void testValidOffsetHeader() { return v; }); PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -130,8 +129,8 @@ public void testValidOffsetWithBodySerializer() { OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -151,8 +150,8 @@ public void testValidOffsetSingleBodyParamIntType() { OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -172,8 +171,8 @@ public void testValidOffsetSingleBodyParamStringType() { OffsetPagination offset = new OffsetPagination("$request.body"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -195,8 +194,8 @@ public void testValidOffsetSingleBodyParam() { OffsetPagination offset = new OffsetPagination("$request.body#/offset"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -218,8 +217,8 @@ public void testValidOffsetMultipleBodyParams() { OffsetPagination offset = new OffsetPagination("$request.body#/offset"); HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); + PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } From d805d01923d3725714b607ffac219c2dd8a66430 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 12:08:15 +0500 Subject: [PATCH 22/33] test(pagination): sonarcube refactor(1) --- src/test/java/apimatic/core/RequestBuilderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index 1d360590..644939a6 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -173,10 +173,10 @@ public void testUpdateSimpleQueryParam() throws IOException { @Test public void testUpdateComplexQueryParam() throws IOException { - HttpRequest.Builder requestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) + HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .queryParam(param -> param.key("que").value(new Atom(12, 12))); - updateAndVerify(requestBuilder, "$request.query#/que/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", 12, 14); } @Test From 319ba7e8a77b4d67098da360ead9efde99cdaaeb Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 12 Jun 2025 12:11:00 +0500 Subject: [PATCH 23/33] fix java 11 docs issues --- .../apimatic/core/types/pagination/CheckedSupplier.java | 8 ++++---- .../io/apimatic/core/types/pagination/PaginatedData.java | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java index 479a48b1..5d239e10 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java +++ b/src/main/java/io/apimatic/core/types/pagination/CheckedSupplier.java @@ -9,7 +9,7 @@ public interface CheckedSupplier { /** * @param Represents type of stored value. * @param Represents type of stored exception. - * @param value Create an instance with stored exception of type E. + * @param exception Create an instance with stored exception of type E. * @return created CheckedSupplier object. */ @SuppressWarnings("unchecked") @@ -52,10 +52,10 @@ public T get() throws E, IOException { } /** - * Get the stored instance or throw an exception. + * Get the stored instance or throw a stored exception. * @return The stored instance of type T. - * @throws E - * @throws IOException + * @throws E The stored CoreApiException. + * @throws IOException The stored IOException. */ T get() throws E, IOException; } diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 8d152160..07f7fe73 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -68,13 +68,13 @@ public int getPageSize() { /** * Get the items in current page converted to type T. * @param Represents the type of items in list. - * @param pageSupplier A converter to convert the CheckedSupplier of items to type T. + * @param itemSupplier A converter to convert the CheckedSupplier of items to type T. * @return All items in current page converted via itemSupplier. */ - public List getItems(Function, T> itemCreator) { + public List getItems(Function, T> itemSupplier) { List convertedItems = new ArrayList<>(); for (CheckedSupplier i : this.items) { - convertedItems.add(itemCreator.apply(i)); + convertedItems.add(itemSupplier.apply(i)); } return convertedItems; From 4bf557663b6bf0952c8b1e3f86cbfc5ad0de8a0d Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 14:14:39 +0500 Subject: [PATCH 24/33] test(pagination): fix linting issues --- .../core/types/pagination/PaginatedData.java | 4 +- .../apimatic/core/utilities/CoreHelper.java | 2 +- src/test/java/apimatic/core/EndToEndTest.java | 19 +++- .../apimatic/core/RequestBuilderTest.java | 66 ++++++++----- .../type/pagination/CheckedSupplierTest.java | 11 ++- .../type/pagination/CursorPaginationTest.java | 76 ++++----------- .../type/pagination/LinkPaginationTest.java | 24 ++--- .../type/pagination/OffsetPaginationTest.java | 28 +++--- .../type/pagination/PagePaginationTest.java | 4 +- .../type/pagination/PaginatedDataTest.java | 96 ++++++++++--------- .../core/utilities/CoreHelperTest.java | 19 ++-- 11 files changed, 177 insertions(+), 172 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 07f7fe73..61dad642 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -165,8 +165,8 @@ public PaginatedData copy() { } /** - * Start fetching the next page asynchronously. - * @return A CompletableFuture of boolean instance suggesting if there is a next page or not. + * Start fetching the next page asynchronously. + * @return A CompletableFuture of boolean instance suggesting if there is a next page or not. */ public CompletableFuture fetchNextPageAsync() { if (dataClosed) { diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index ddf1c796..d655763c 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -1779,7 +1779,7 @@ private static JsonValue toJsonValue(Object obj) { return Json.createValue((Double) obj); } if (obj instanceof Boolean) { - return Boolean.TRUE.equals(obj) ? JsonValue.TRUE : JsonValue.FALSE; // ✅ Safe & compliant + return Boolean.TRUE.equals(obj) ? JsonValue.TRUE : JsonValue.FALSE; } return null; } diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index 71a819bc..f1fbd2e9 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -122,8 +122,14 @@ public class EndToEndTest extends MockCoreConfig { * Mock of {@link Response}. */ @Mock - protected Response response; + private Response response; + protected Response getResponse() { + return response; + } + protected void setResponse(Response response) { + this.response = response; + } /** * Mock of {@link Request}. @@ -500,7 +506,16 @@ private ApiCall getApiCallGlobalErrorTemplateWithHeade .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) .build(); } - + + /** + * Creates a global configuration instance with the provided callback. + * This method is designed for extension by subclasses to customize global configuration. + * Subclasses should override this method and call super.getGlobalConfig(callback) + * to maintain base functionality while adding custom configurations. + * + * @param callback The callback instance to use in the configuration + * @return A fully configured GlobalConfiguration instance + */ protected GlobalConfiguration getGlobalConfig(Callback callback) { String userAgent = "APIMATIC 3.0"; GlobalConfiguration globalConfig = new GlobalConfiguration.Builder() diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index 644939a6..46a79b61 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -60,6 +60,21 @@ public class RequestBuilderTest extends MockCoreConfig { + private static final int ELECTRONS_DEFAULT = 12; + private static final int ELECTRONS_UPDATED = 14; + private static final long QUERY_DEFAULT = 234L; + private static final long QUERY_UPDATED = 254L; + private static final double HEADER_DEFAULT = 2.14; + private static final double HEADER_UPDATED = 19.95; + private static final int ATOM_NUMBER = 23; + private static final int ATOM_MASS = 24; + private static final int BODY_A_ELECTRONS = 2; + private static final int BODY_A_MASS = 4; + private static final int BODY_B_ELECTRONS = 4; + private static final int BODY_B_MASS = 2; + private static final int BODY_B_UPDATED = 8; + + /** * Initializes mocks annotated with Mock. */ @@ -142,7 +157,7 @@ public void testUpdatePathParam() throws IOException { public void testUpdatePathParamArray() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .templateParam(param -> param.key("temp") - .value(new String[] { "apple", "banana", "cherry" })); + .value(new String[] {"apple", "banana", "cherry" })); updateAndVerify(localRequestBuilder, "$request.path#/temp/1", "banana", "mango"); } @@ -158,41 +173,41 @@ public void testUpdateSimpleFormParam() throws IOException { @Test public void testUpdateComplexFormParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .formParam(param -> param.key("form").value(new Atom(12, 12))); + .formParam(param -> param.key("form").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test public void testUpdateSimpleQueryParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .queryParam(param -> param.key("que").value(234L)); + .queryParam(param -> param.key("que").value(QUERY_DEFAULT)); - updateAndVerify(localRequestBuilder, "$request.query#/que", 234L, 254L); + updateAndVerify(localRequestBuilder, "$request.query#/que", QUERY_DEFAULT, QUERY_UPDATED); } @Test public void testUpdateComplexQueryParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .queryParam(param -> param.key("que").value(new Atom(12, 12))); + .queryParam(param -> param.key("que").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test public void testUpdateSimpleHeaderParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .headerParam(param -> param.key("head").value(2.14)); + .headerParam(param -> param.key("head").value(HEADER_DEFAULT)); - updateAndVerify(localRequestBuilder, "$request.headers#/head", 2.14, 19.95); + updateAndVerify(localRequestBuilder, "$request.headers#/head", HEADER_DEFAULT, HEADER_UPDATED); } @Test public void testUpdateComplexHeaderParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .headerParam(param -> param.key("head").value(new Atom(12, 12))); + .headerParam(param -> param.key("head").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", 12, 14); + updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -207,18 +222,18 @@ public void testUpdateMultipleBodyParams() throws IOException { @Test public void testUpdateComplexBodyParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) - .bodyParam(param -> param.value(new Atom(23, 24))); + .bodyParam(param -> param.value(new Atom(ATOM_NUMBER, ATOM_MASS))); - updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", 23, 24); + updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", ATOM_NUMBER, ATOM_MASS); } @Test public void testUpdateMultipleComplexBodyParams() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) - .bodyParam(param -> param.key("bodyA").value(new Atom(2, 4))) - .bodyParam(param -> param.key("bodyB").value(new Atom(4, 2))); + .bodyParam(param -> param.key("bodyA").value(new Atom(BODY_A_ELECTRONS, BODY_A_MASS))) + .bodyParam(param -> param.key("bodyB").value(new Atom(BODY_B_ELECTRONS, BODY_B_MASS))); - updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", 4, 8); + updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", BODY_B_ELECTRONS, BODY_B_UPDATED); } @Test @@ -230,14 +245,12 @@ public void testUpdateSimpleBodyParam() throws IOException { } private void updateAndVerify(HttpRequest.Builder requestBuilder, String pointer, Object oldValue, Object newValue) { - requestBuilder.updateParameterByJsonPointer(pointer, old -> - { + requestBuilder.updateParameterByJsonPointer(pointer, old -> { assertEquals(oldValue.toString(), old.toString()); return newValue; }); AtomicBoolean asserted = new AtomicBoolean(false); - requestBuilder.updateParameterByJsonPointer(pointer, newV -> - { + requestBuilder.updateParameterByJsonPointer(pointer, newV -> { assertEquals(newValue.toString(), newV.toString()); asserted.set(true); return newV; @@ -248,17 +261,19 @@ private void updateAndVerify(HttpRequest.Builder requestBuilder, String pointer, @Test(expected = NullPointerException.class) public void testBodyParamValidation() throws IOException { // when - new HttpRequest.Builder().httpMethod(Method.POST).bodyParam(param -> param.value(null)) + new HttpRequest.Builder() + .httpMethod(Method.POST) + .bodyParam(param -> param.value(null)) .build(getMockGlobalConfig()); - } @Test(expected = NullPointerException.class) public void testBodyParamValidation1() throws IOException { // when - new HttpRequest.Builder().httpMethod(Method.POST).bodyParam(param -> param.value(null)) + new HttpRequest.Builder() + .httpMethod(Method.POST) + .bodyParam(param -> param.value(null)) .build(getMockGlobalConfig()); - } @Test @@ -266,7 +281,8 @@ public void testBodyParam() throws IOException { // when Request coreHttpRequest = new HttpRequest.Builder().httpMethod(Method.PATCH) - .bodyParam(param -> param.value("bodyValue")).build(getMockGlobalConfig()); + .bodyParam(param -> param.value("bodyValue")) + .build(getMockGlobalConfig()); when(coreHttpRequest.getBody()).thenReturn("bodyValue"); diff --git a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java index 2a058d43..45ccba37 100644 --- a/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java +++ b/src/test/java/apimatic/core/type/pagination/CheckedSupplierTest.java @@ -1,7 +1,7 @@ package apimatic.core.type.pagination; -import static org.junit.Assert.*; import org.junit.Test; +import static org.junit.Assert.assertNull; import java.io.IOException; @@ -13,7 +13,8 @@ public class CheckedSupplierTest { @Test(expected = IOException.class) public void testCreateErrorWithIOException() throws Exception { IOException ioException = new IOException("Test IO Exception"); - CheckedSupplier supplier = CheckedSupplier.createError(ioException); + CheckedSupplier supplier = + CheckedSupplier.createError(ioException); @SuppressWarnings("unused") String value = supplier.get(); } @@ -21,7 +22,8 @@ public void testCreateErrorWithIOException() throws Exception { @Test(expected = CoreApiException.class) public void testCreateErrorWithCoreApiException() throws Exception { CoreApiException apiException = new CoreApiException("Test API Exception"); - CheckedSupplier supplier = CheckedSupplier.createError(apiException); + CheckedSupplier supplier = + CheckedSupplier.createError(apiException); @SuppressWarnings("unused") String value = supplier.get(); } @@ -29,7 +31,8 @@ public void testCreateErrorWithCoreApiException() throws Exception { @Test public void testCreateErrorWithUnsupportedException() { RuntimeException runtimeException = new RuntimeException("Test Exception"); - CheckedSupplier supplier = CheckedSupplier.createError(runtimeException); + CheckedSupplier supplier = + CheckedSupplier.createError(runtimeException); assertNull(supplier); } } diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 9aed7e79..56e63c03 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -18,19 +18,11 @@ import io.apimatic.core.types.pagination.PaginatedData; import io.apimatic.coreinterfaces.http.response.Response; -/** - * Unit tests for the {@link CursorPagination} class. - * This class tests the behavior of cursor pagination logic. - */ public class CursorPaginationTest { - /** - * Silent rule for Mockito initialization. - */ @Rule public MockitoRule initRule = MockitoJUnit.rule().silent(); - // Constants private static final String CURSOR_KEY = "cursor"; private static final String RESPONSE_POINTER_VALID = "$response.body#/next_cursor"; private static final String RESPONSE_POINTER_INVALID = "$response.body#/next"; @@ -40,17 +32,13 @@ public class CursorPaginationTest { private static final String NEXT_CURSOR_JSON_STRING = "{\"next_cursor\": \"xyz123\"}"; private static final String NEXT_CURSOR_JSON_INT = "{\"next_cursor\": 123}"; - /** - * Test with a valid cursor value. - */ @Test public void testWithValidCursor() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); @@ -67,17 +55,13 @@ public void testWithValidCursor() { assertEquals("xyz123", pageWrapper.getCursorInput()); } - /** - * Test with a valid cursor but different response type (integer). - */ @Test public void testWithValidCursorAndDifferentTypeA() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); @@ -94,18 +78,14 @@ public void testWithValidCursorAndDifferentTypeA() { assertEquals("123", pageWrapper.getCursorInput()); } - /** - * Test with a valid cursor but different response type (integer) and integer cursor input. - */ @Test public void testWithValidCursorAndDifferentType() { PaginatedData paginatedData = mock(PaginatedData.class); final int current = 456; Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key(CURSOR_KEY).value(current))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key(CURSOR_KEY).value(current))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); @@ -122,9 +102,6 @@ public void testWithValidCursorAndDifferentType() { assertEquals("123", pageWrapper.getCursorInput()); } - /** - * Test with valid cursor but missing in the first request. - */ @Test public void testWithValidCursorButMissingInFirstRequest() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -140,17 +117,13 @@ public void testWithValidCursorButMissingInFirstRequest() { assertNull(requestBuilder); } - /** - * Test with valid cursor from a response body and different type. - */ @Test public void testWithValidCursorFromResponseBody() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam( - q -> q.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().queryParam(q -> q.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); @@ -167,17 +140,13 @@ public void testWithValidCursorFromResponseBody() { assertEquals("123", pageWrapper.getCursorInput()); } - /** - * Test case where the response pointer is invalid. - */ @Test public void testWithInvalidResponsePointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().headerParam(h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); @@ -187,17 +156,13 @@ public void testWithInvalidResponsePointer() { assertNull(requestBuilder); } - /** - * Test with missing response pointer. - */ @Test public void testWithMissingResponsePointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().headerParam(h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); @@ -207,17 +172,13 @@ public void testWithMissingResponsePointer() { assertNull(requestBuilder); } - /** - * Test case with invalid request pointer. - */ @Test public void testWithInvalidRequestPointer() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().headerParam(h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); @@ -226,22 +187,17 @@ public void testWithInvalidRequestPointer() { assertNull(cursor.apply(paginatedData)); } - /** - * Test with missing request pointer. - */ @Test public void testWithMissingRequest() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); - when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().headerParam( - h -> h.key(CURSOR_KEY).value("abc"))); + when(paginatedData.getRequestBuilder()).thenReturn( + new HttpRequest.Builder().headerParam(h -> h.key(CURSOR_KEY).value("abc"))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, null); assertNull(cursor.apply(paginatedData)); } - } diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index 96f251dd..a1972316 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -27,17 +27,19 @@ */ public class LinkPaginationTest { - private static final String RESPONSE_BODY_POINTER = "$response.body#/next"; - private static final String RESPONSE_HEADERS_POINTER = "$response.headers#/next"; - private static final String REQUEST_QUERY_PAGE = "$request.query#/page"; - private static final String REQUEST_QUERY_SIZE = "$request.query#/size"; - private static final String REQUEST_HEADERS_PAGE = "$request.headers#/page"; - private static final String NEXT_URL_SINGLE = "https://api.example.com?page=2"; - private static final String NEXT_URL_MULTIPLE = "https://api.example.com?page=2&size=5"; - private static final String NEXT_URL_ENCODED = "https://api.example.com?page%20o=2%20a&size%20q=5^%214$#"; - private static final String PAGE = "page"; - private static final String SIZE = "size"; - private static final String NEXT = "next"; + private static final String RESPONSE_BODY_POINTER = "$response.body#/next"; + private static final String RESPONSE_HEADERS_POINTER = "$response.headers#/next"; + private static final String REQUEST_QUERY_PAGE = "$request.query#/page"; + private static final String REQUEST_QUERY_SIZE = "$request.query#/size"; + private static final String REQUEST_HEADERS_PAGE = "$request.headers#/page"; + private static final String NEXT_URL_SINGLE = "https://api.example.com?page=2"; + private static final String NEXT_URL_MULTIPLE = "https://api.example.com?page=2&size=5"; + private static final String NEXT_URL_ENCODED = + "https://api.example.com?page%20o=2%20a&size%20q=5^%214$#"; + private static final String PAGE = "page"; + private static final String SIZE = "size"; + private static final String NEXT = "next"; + /** * Silent rule for Mockito initialization. diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index bbbb3872..bd51decb 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -30,9 +30,9 @@ public class OffsetPaginationTest { private static final int INITIAL_OFFSET = 3; private static final String INITIAL_OFFSET_STRING = "3"; private static final int PAGE_SIZE = 100; - private static final int OFFSET_PLUS_PAGE = INITIAL_OFFSET + PAGE_SIZE; + private static final int OFFSET_PLUS_PAGE = INITIAL_OFFSET + PAGE_SIZE; private static final int OFFSET_VAL = 1; - private static final int OFFSET_VAL_PLUS_ONE = OFFSET_VAL + PAGE_SIZE; + private static final int OFFSET_VAL_PLUS_ONE = OFFSET_VAL + PAGE_SIZE; private static final int OFFSET_STRING = 5; private static final int OFFSET_STRING_PLUS_PAGE = OFFSET_STRING + PAGE_SIZE; private static final int NUMERIC_OFFSET = 5; @@ -69,8 +69,8 @@ public void testValidOffsetHeader() { PageWrapper pageWrapper = PageWrapper.create(response, null, null); offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); - } - + } + @Test public void testBodyWithTypeCoreFileWrapper() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -99,7 +99,7 @@ public void testValidOffsetWithBodySerializerIOException() { HttpRequest.Builder builder = new HttpRequest.Builder() .bodyParam(b -> b.value(body)) - .bodySerializer(() -> { + .bodySerializer(() -> { throw new IOException("Simulated IOException"); }); @@ -114,7 +114,7 @@ public void testValidOffsetWithBodySerializerIOException() { assertNull(requestBuilder); } - + @Test public void testValidOffsetWithBodySerializer() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -134,14 +134,14 @@ public void testValidOffsetWithBodySerializer() { offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } - + @Test public void testValidOffsetSingleBodyParamIntType() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); HttpRequest.Builder builder = new HttpRequest.Builder() - .bodyParam(b -> b.value(INITIAL_OFFSET)); + .bodyParam(b -> b.value(INITIAL_OFFSET)); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); @@ -155,14 +155,14 @@ public void testValidOffsetSingleBodyParamIntType() { offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } - + @Test public void testValidOffsetSingleBodyParamStringType() { PaginatedData paginatedData = mock(PaginatedData.class); Response response = mock(Response.class); HttpRequest.Builder builder = new HttpRequest.Builder() - .bodyParam(b -> b.value(INITIAL_OFFSET_STRING)); + .bodyParam(b -> b.value(INITIAL_OFFSET_STRING)); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); @@ -176,7 +176,7 @@ public void testValidOffsetSingleBodyParamStringType() { offset.addMetaData(pageWrapper); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } - + @Test public void testValidOffsetSingleBodyParam() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -185,7 +185,7 @@ public void testValidOffsetSingleBodyParam() { Map body = new HashMap<>(); body.put("offset", INITIAL_OFFSET); HttpRequest.Builder builder = new HttpRequest.Builder() - .bodyParam(b -> b.value(body)); + .bodyParam(b -> b.value(body)); when(paginatedData.getRequestBuilder()).thenReturn(builder); when(paginatedData.getResponse()).thenReturn(response); @@ -346,7 +346,7 @@ public void testInvalidStringOffset() { }); PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); - assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); // Assuming invalid offset sets to default -1 + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @Test @@ -362,7 +362,7 @@ public void testMissingOffset() { assertNotNull(requestBuilder); PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); offset.addMetaData(pageWrapper); - assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); + assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @Test diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 3d6d8fcf..242a81d3 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -173,7 +173,9 @@ public void testWithInvalidStringPage() { Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) - .thenReturn(new HttpRequest.Builder().queryParam(q -> q.key("page").value(INVALID_PAGE_STRING))); + .thenReturn(new HttpRequest.Builder() + .queryParam(q -> q.key("page") + .value(INVALID_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn("{\"page\": \"" + INVALID_PAGE_STRING + "\"}"); diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index a92a3de9..d22e43b8 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -43,11 +43,17 @@ @RunWith(Parameterized.class) public class PaginatedDataTest extends EndToEndTest { + private static final int NOT_FOUND_STATUS_CODE = 404; + private static final int SUCCESS_STATUS_CODE = 200; + private static final int PAGE_SIZE = 3; + /** + * Tests pagination with failing response (404 status code) + */ @Test public void testInvalidPaginationWithFailingResponse() throws IOException { Runnable call1 = () -> { - when(response.getStatusCode()).thenReturn(404); + when(getResponse().getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); }; PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, @@ -55,16 +61,19 @@ public void testInvalidPaginationWithFailingResponse() throws IOException { Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); Exception exception = assertThrows(CoreApiException.class, pages.next()::get); assertEquals("Not found", exception.getMessage()); assertFalse(pages.hasNext()); } + /** + * Tests async pagination with failing response (404 status code) + */ @Test public void testInvalidPaginationWithFailingResponseAsync() throws IOException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(response.getStatusCode()).thenReturn(404); + Runnable call1 = () -> when(getResponse().getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); @@ -78,27 +87,28 @@ public void testInvalidPaginationWithFailingResponseAsync() hasNext = paginatedData.fetchNextPageAsync().get(); assertFalse(hasNext); } + @Parameters public static List invalidPaginationTestCases() { return Arrays.asList(new Object[][] { - // Test case 1: Empty items { "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}" }, - // Test case 2: Null items { "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}" }, - // Test case 3: Null page { null } }); } private final String responseBody; - public PaginatedDataTest(String responseBody) { + public PaginatedDataTest(final String responseBody) { this.responseBody = responseBody; } + /** + * Tests invalid pagination scenarios + */ @Test public void testInvalidPagination() throws IOException { - Runnable call1 = () -> when(response.getBody()).thenReturn(responseBody); + Runnable call1 = () -> when(getResponse().getBody()).thenReturn(responseBody); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); @@ -107,15 +117,15 @@ public void testInvalidPagination() throws IOException { @Test public void testLinkPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, @@ -124,7 +134,7 @@ public void testLinkPaginationData() throws IOException, CoreApiException { @Test public void testInvalidLinkPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, @@ -133,22 +143,22 @@ public void testInvalidLinkPaginationData() throws IOException, CoreApiException Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); assertFalse(pages.hasNext()); } @Test public void testCursorPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, @@ -158,7 +168,7 @@ public void testCursorPaginationData() throws IOException, CoreApiException { @Test public void testInvalidCursorPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, @@ -167,7 +177,7 @@ public void testInvalidCursorPaginationData() throws IOException, CoreApiExcepti Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); assertFalse(pages.hasNext()); @@ -175,22 +185,22 @@ public void testInvalidCursorPaginationData() throws IOException, CoreApiExcepti new CursorPagination("$response.body#/page_info", "")); pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); assertFalse(pages.hasNext()); } @Test public void testOffsetPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, @@ -199,7 +209,7 @@ public void testOffsetPaginationData() throws IOException, CoreApiException { @Test public void testInvalidOffsetPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, @@ -208,22 +218,22 @@ public void testInvalidOffsetPaginationData() throws IOException, CoreApiExcepti Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); assertFalse(pages.hasNext()); } @Test public void testPagePaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, @@ -232,7 +242,7 @@ public void testPagePaginationData() throws IOException, CoreApiException { @Test public void testInvalidPagePaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, @@ -241,34 +251,33 @@ public void testInvalidPagePaginationData() throws IOException, CoreApiException Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); - assertTrue(pages.hasNext()); // checking hasNext twice should have the same affect + assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); assertFalse(pages.hasNext()); } @Test public void testMultiPaginationData() throws IOException, CoreApiException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"," + "\"next_link\":\"https://localhost:3000/path?page=2\"}"); - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); verifyData(getPaginatedData(call1, call2, call3, new LinkPagination("$response.INVALID#/next_link"), - // invalid next link pointer so 2nd call will be made using page pagination new PagePagination("$request.body#/limit"))); } @Test public void testInvalidPaginationDataAsync() throws IOException, CoreApiException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, @@ -287,15 +296,14 @@ public void testInvalidPaginationDataAsync() @Test public void testMultiPaginationDataAsync() throws IOException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(response.getBody()).thenReturn( + Runnable call1 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"apple\",\"mango\",\"orange\"],\"" + "page_info\":\"fruits\"}"); - // doesn't have next_link to force making the next calls using page pagination - Runnable call2 = () -> when(response.getBody()).thenReturn( + Runnable call2 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[\"potato\",\"carrot\",\"tomato\"]," + "\"page_info\":\"vegitables\"," + "\"next_link\":\"https://localhost:3000/path?page=3\"}"); - Runnable call3 = () -> when(response.getBody()).thenReturn( + Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); PaginatedData, @@ -330,10 +338,10 @@ public void testMultiPaginationDataAsync() PageWrapper page = paginatedData.getPage(pageCreator); assertNull(page); - assertEquals(0, paginatedData.getItems(itemCreator).size()); // Changed here + assertEquals(0, paginatedData.getItems(itemCreator).size()); boolean hasNext = paginatedData.fetchNextPageAsync().get(); - assertEquals(true, hasNext); // Changed here + assertEquals(true, hasNext); assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); assertEquals(expectedPage1.getData(), page.getResult().getData()); @@ -343,7 +351,7 @@ public void testMultiPaginationDataAsync() assertEquals(-1, page.getPageInput()); hasNext = paginatedData.fetchNextPageAsync().get(); - assertEquals(true, hasNext); // Changed here + assertEquals(true, hasNext); assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); assertEquals(expectedPage2.getData(), page.getResult().getData()); @@ -353,10 +361,10 @@ public void testMultiPaginationDataAsync() assertEquals(2, page.getPageInput()); hasNext = paginatedData.fetchNextPageAsync().get(); - assertEquals(false, hasNext); // Changed here + assertEquals(false, hasNext); page = paginatedData.getPage(pageCreator); assertNull(page); - assertEquals(0, paginatedData.getItems(itemCreator).size()); // Changed here + assertEquals(0, paginatedData.getItems(itemCreator).size()); } private void verifyData(PaginatedData, @@ -432,7 +440,7 @@ private void verifyData(PaginatedData, RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, Runnable call3, PaginationStrategy... pagination) throws IOException { - when(response.getHeaders()).thenReturn(getHttpHeaders()); + when(getResponse().getHeaders()).thenReturn(getHttpHeaders()); Callback pageCallback = new Callback() { private int callNumber = 1; @Override diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index e41b8ee3..3df1c6c5 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -75,6 +75,9 @@ public class CoreHelperTest { + private static final int NUMBER_OF_PROTONS = 14; + private static final int NUMBER_OF_NEUTRONS = 26; + private static final int UPDATED_VALUE = 23; private static final int YEAR2010 = 2010; private static final double PRECISION_NUMBER = 1.2; private static final int YEAR3 = 2020; @@ -1853,17 +1856,17 @@ public void testUpdateValueByPointerWithInvalidInput() { @Test public void testUpdateValueByPointerInvalidCases() { Map atoms = new HashMap<>(); - atoms.put("atom", new Atom(14, 26)); - atoms.put("nucleas", new Atom(14, null)); + atoms.put("atom", new Atom(NUMBER_OF_PROTONS, NUMBER_OF_NEUTRONS)); + atoms.put("nucleas", new Atom(NUMBER_OF_PROTONS, null)); Map result = CoreHelper.updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); - assertEquals(atoms, result); // update by null not allowed - - result = CoreHelper.updateValueByPointer(atoms, "/nucleas/NumberOfProtons", v -> 26); - assertEquals(atoms, result); // updating null values not allowed + assertEquals(atoms, result); - result = CoreHelper.updateValueByPointer(atoms, "/atom", v -> new Atom(23, 23)); - assertEquals(atoms, result); // update full object not allowed + result = CoreHelper.updateValueByPointer(atoms, "/nucleas/NumberOfProtons", v -> NUMBER_OF_NEUTRONS); + assertEquals(atoms, result); + + result = CoreHelper.updateValueByPointer(atoms, "/atom", v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); + assertEquals(atoms, result); } private Map getComplexType() throws IOException { From be69a5beb8124a6a3b209a9b64d26db076dac4a2 Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 14:50:53 +0500 Subject: [PATCH 25/33] test(pagination): fix linting issues(2) --- src/test/java/apimatic/core/EndToEndTest.java | 15 +++- .../apimatic/core/RequestBuilderTest.java | 61 +++++++++------- .../type/pagination/CursorPaginationTest.java | 21 ++++-- .../type/pagination/LinkPaginationTest.java | 24 +++---- .../type/pagination/OffsetPaginationTest.java | 2 +- .../type/pagination/PaginatedDataTest.java | 72 +++++++++++-------- .../core/utilities/CoreHelperTest.java | 17 +++-- 7 files changed, 129 insertions(+), 83 deletions(-) diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index f1fbd2e9..fb40cca6 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -123,10 +123,23 @@ public class EndToEndTest extends MockCoreConfig { */ @Mock private Response response; + /** + * Base test class for end-to-end tests. + *

+ * Subclasses can override this method to provide a custom response retrieval mechanism. + * If overridden, ensure thread-safety and proper response management. + */ protected Response getResponse() { return response; } + /** + * Allows subclasses to customize how the response is set. + *

+ * If overridden, ensure that the response is correctly handled and does not introduce memory leaks. + * + * @param response The response to set. + */ protected void setResponse(Response response) { this.response = response; } @@ -506,7 +519,7 @@ private ApiCall getApiCallGlobalErrorTemplateWithHeade .hasBinaryResponse(false).retryOption(RetryOption.DEFAULT)) .build(); } - + /** * Creates a global configuration instance with the provided callback. * This method is designed for extension by subclasses to customize global configuration. diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index 46a79b61..70640672 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -60,19 +60,19 @@ public class RequestBuilderTest extends MockCoreConfig { - private static final int ELECTRONS_DEFAULT = 12; - private static final int ELECTRONS_UPDATED = 14; - private static final long QUERY_DEFAULT = 234L; - private static final long QUERY_UPDATED = 254L; - private static final double HEADER_DEFAULT = 2.14; - private static final double HEADER_UPDATED = 19.95; - private static final int ATOM_NUMBER = 23; - private static final int ATOM_MASS = 24; - private static final int BODY_A_ELECTRONS = 2; - private static final int BODY_A_MASS = 4; - private static final int BODY_B_ELECTRONS = 4; - private static final int BODY_B_MASS = 2; - private static final int BODY_B_UPDATED = 8; + private static final int ELECTRONS_DEFAULT = 12; + private static final int ELECTRONS_UPDATED = 14; + private static final long QUERY_DEFAULT = 234L; + private static final long QUERY_UPDATED = 254L; + private static final double HEADER_DEFAULT = 2.14; + private static final double HEADER_UPDATED = 19.95; + private static final int ATOM_NUMBER = 23; + private static final int ATOM_MASS = 24; + private static final int BODY_A_ELECTRONS = 2; + private static final int BODY_A_MASS = 4; + private static final int BODY_B_ELECTRONS = 4; + private static final int BODY_B_MASS = 2; + private static final int BODY_B_UPDATED = 8; /** @@ -173,9 +173,11 @@ public void testUpdateSimpleFormParam() throws IOException { @Test public void testUpdateComplexFormParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .formParam(param -> param.key("form").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .formParam(param -> param.key("form") + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -189,9 +191,11 @@ public void testUpdateSimpleQueryParam() throws IOException { @Test public void testUpdateComplexQueryParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .queryParam(param -> param.key("que").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .queryParam(param -> param.key("que") + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -205,9 +209,11 @@ public void testUpdateSimpleHeaderParam() throws IOException { @Test public void testUpdateComplexHeaderParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) - .headerParam(param -> param.key("head").value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .headerParam(param -> param.key("head") + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); - updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -224,16 +230,20 @@ public void testUpdateComplexBodyParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.value(new Atom(ATOM_NUMBER, ATOM_MASS))); - updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", ATOM_NUMBER, ATOM_MASS); + updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", + ATOM_NUMBER, ATOM_MASS); } @Test public void testUpdateMultipleComplexBodyParams() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) - .bodyParam(param -> param.key("bodyA").value(new Atom(BODY_A_ELECTRONS, BODY_A_MASS))) - .bodyParam(param -> param.key("bodyB").value(new Atom(BODY_B_ELECTRONS, BODY_B_MASS))); + .bodyParam(param -> param.key("bodyA") + .value(new Atom(BODY_A_ELECTRONS, BODY_A_MASS))) + .bodyParam(param -> param.key("bodyB") + .value(new Atom(BODY_B_ELECTRONS, BODY_B_MASS))); - updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", BODY_B_ELECTRONS, BODY_B_UPDATED); + updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", + BODY_B_ELECTRONS, BODY_B_UPDATED); } @Test @@ -244,7 +254,8 @@ public void testUpdateSimpleBodyParam() throws IOException { updateAndVerify(localRequestBuilder, "$request.body", "BodyValue", "new Value"); } - private void updateAndVerify(HttpRequest.Builder requestBuilder, String pointer, Object oldValue, Object newValue) { + private void updateAndVerify(HttpRequest.Builder requestBuilder, + String pointer, Object oldValue, Object newValue) { requestBuilder.updateParameterByJsonPointer(pointer, old -> { assertEquals(oldValue.toString(), old.toString()); return newValue; @@ -289,7 +300,7 @@ public void testBodyParam() throws IOException { // verify assertEquals(coreHttpRequest.getBody(), "bodyValue"); } - + @Test public void testClonedBodyParam() throws IOException { // when diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 56e63c03..dc704a3b 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -42,7 +42,8 @@ public void testWithValidCursor() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -65,7 +66,8 @@ public void testWithValidCursorAndDifferentTypeA() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -89,7 +91,8 @@ public void testWithValidCursorAndDifferentType() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -111,7 +114,8 @@ public void testWithValidCursorButMissingInFirstRequest() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); @@ -127,7 +131,8 @@ public void testWithValidCursorFromResponseBody() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_QUERY_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -150,7 +155,8 @@ public void testWithInvalidResponsePointer() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_INVALID, REQUEST_HEADER_POINTER); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_INVALID, + REQUEST_HEADER_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); @@ -182,7 +188,8 @@ public void testWithInvalidRequestPointer() { when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); - CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_POINTER_INVALID); + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_POINTER_INVALID); assertNull(cursor.apply(paginatedData)); } diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index a1972316..fb8c66e1 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -27,18 +27,18 @@ */ public class LinkPaginationTest { - private static final String RESPONSE_BODY_POINTER = "$response.body#/next"; - private static final String RESPONSE_HEADERS_POINTER = "$response.headers#/next"; - private static final String REQUEST_QUERY_PAGE = "$request.query#/page"; - private static final String REQUEST_QUERY_SIZE = "$request.query#/size"; - private static final String REQUEST_HEADERS_PAGE = "$request.headers#/page"; - private static final String NEXT_URL_SINGLE = "https://api.example.com?page=2"; - private static final String NEXT_URL_MULTIPLE = "https://api.example.com?page=2&size=5"; - private static final String NEXT_URL_ENCODED = - "https://api.example.com?page%20o=2%20a&size%20q=5^%214$#"; - private static final String PAGE = "page"; - private static final String SIZE = "size"; - private static final String NEXT = "next"; + private static final String RESPONSE_BODY_POINTER = "$response.body#/next"; + private static final String RESPONSE_HEADERS_POINTER = "$response.headers#/next"; + private static final String REQUEST_QUERY_PAGE = "$request.query#/page"; + private static final String REQUEST_QUERY_SIZE = "$request.query#/size"; + private static final String REQUEST_HEADERS_PAGE = "$request.headers#/page"; + private static final String NEXT_URL_SINGLE = "https://api.example.com?page=2"; + private static final String NEXT_URL_MULTIPLE = "https://api.example.com?page=2&size=5"; + private static final String NEXT_URL_ENCODED = + "https://api.example.com?page%20o=2%20a&size%20q=5^%214$#"; + private static final String PAGE = "page"; + private static final String SIZE = "size"; + private static final String NEXT = "next"; /** diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index bd51decb..05fb8858 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -32,7 +32,7 @@ public class OffsetPaginationTest { private static final int PAGE_SIZE = 100; private static final int OFFSET_PLUS_PAGE = INITIAL_OFFSET + PAGE_SIZE; private static final int OFFSET_VAL = 1; - private static final int OFFSET_VAL_PLUS_ONE = OFFSET_VAL + PAGE_SIZE; + private static final int OFFSET_VAL_PLUS_ONE = OFFSET_VAL + PAGE_SIZE; private static final int OFFSET_STRING = 5; private static final int OFFSET_STRING_PLUS_PAGE = OFFSET_STRING + PAGE_SIZE; private static final int NUMERIC_OFFSET = 5; diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index d22e43b8..22eb1784 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -73,7 +73,8 @@ public void testInvalidPaginationWithFailingResponse() throws IOException { @Test public void testInvalidPaginationWithFailingResponseAsync() throws IOException, InterruptedException, ExecutionException { - Runnable call1 = () -> when(getResponse().getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); + Runnable call1 = () -> when(getResponse() + .getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); @@ -88,17 +89,27 @@ public void testInvalidPaginationWithFailingResponseAsync() assertFalse(hasNext); } + /** + * Provides invalid pagination test cases to verify handling of edge cases and null responses. + * + * @return A list of invalid pagination test cases, each represented as an object array. + */ @Parameters public static List invalidPaginationTestCases() { return Arrays.asList(new Object[][] { - { "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}" }, - { "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}" }, - { null } + {"{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"}, + {"{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"}, + {null} }); } private final String responseBody; + /** + * Constructs a PaginatedDataTest with the given response body. + * + * @param responseBody the response body string to be used in the test + */ public PaginatedDataTest(final String responseBody) { this.responseBody = responseBody; } @@ -412,7 +423,7 @@ private void verifyData(PaginatedData, assertEquals(expectedNextLink, p.getResult().getNextLink()); assertEquals(expectedData, p.getResult().getData()); assertEquals(expectedData, p.getItems()); - assertEquals(200, p.getStatusCode()); + assertEquals(SUCCESS_STATUS_CODE, p.getStatusCode()); assertEquals(getHttpHeaders(), p.getHeaders()); if (p.getCursorInput() != null && pageOffset > 0) { assertEquals(expectedPages.get(pageOffset - 1).getPageInfo(), p.getCursorInput()); @@ -427,7 +438,7 @@ private void verifyData(PaginatedData, } if (p.getOffsetInput() != -1) { - assertEquals(pageOffset * 3, p.getOffsetInput()); + assertEquals(pageOffset * PAGE_SIZE, p.getOffsetInput()); } pageOffset++; } @@ -437,31 +448,30 @@ private void verifyData(PaginatedData, } private PaginatedData, - RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, - Runnable call3, - PaginationStrategy... pagination) throws IOException { - when(getResponse().getHeaders()).thenReturn(getHttpHeaders()); - Callback pageCallback = new Callback() { - private int callNumber = 1; - @Override - public void onBeforeRequest(Request request) { - if (callNumber == 1) { - call1.run(); - } - else if (callNumber == 2) { - call2.run(); - } - else if (callNumber == 3) { - call3.run(); - callNumber = 0; - } - callNumber++; - } - @Override - public void onAfterResponse(Context context) { - // Does nothing + RecordPage, CoreApiException> getPaginatedData(Runnable call1, Runnable call2, + Runnable call3, + PaginationStrategy... pagination) throws IOException { + when(getResponse().getHeaders()).thenReturn(getHttpHeaders()); + Callback pageCallback = new Callback() { + private int callNumber = 1; + @Override + public void onBeforeRequest(Request request) { + if (callNumber == 1) { + call1.run(); + } else if (callNumber == 2) { + call2.run(); + } else if (callNumber == PAGE_SIZE) { + call3.run(); + callNumber = 0; } - }; + callNumber++; + } + @Override + public void onAfterResponse(Context context) { + // Does nothing + } + }; + return new ApiCall.Builder() .globalConfig(getGlobalConfig(pageCallback)) .requestBuilder(requestBuilder -> requestBuilder.server("https://localhost:3000") @@ -472,7 +482,7 @@ public void onAfterResponse(Context context) { .formParam(param -> param.key("limit").value(1).isRequired(false)) .queryParam(param -> param.key("page").value(1).isRequired(false)) .headerParam(param -> param.key("size").value(2).isRequired(false)) - .headerParam(param -> param.key("size").value(3).isRequired(false)) + .headerParam(param -> param.key("size").value(PAGE_SIZE).isRequired(false)) .headerParam(param -> param.key("accept").value("application/json")) .httpMethod(Method.GET)) .responseHandler(responseHandler -> responseHandler diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 3df1c6c5..442cfc61 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -75,9 +75,9 @@ public class CoreHelperTest { - private static final int NUMBER_OF_PROTONS = 14; - private static final int NUMBER_OF_NEUTRONS = 26; - private static final int UPDATED_VALUE = 23; + private static final int NUMBER_OF_PROTONS = 14; + private static final int NUMBER_OF_NEUTRONS = 26; + private static final int UPDATED_VALUE = 23; private static final int YEAR2010 = 2010; private static final double PRECISION_NUMBER = 1.2; private static final int YEAR3 = 2020; @@ -1859,13 +1859,18 @@ public void testUpdateValueByPointerInvalidCases() { atoms.put("atom", new Atom(NUMBER_OF_PROTONS, NUMBER_OF_NEUTRONS)); atoms.put("nucleas", new Atom(NUMBER_OF_PROTONS, null)); - Map result = CoreHelper.updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); + Map result = CoreHelper + .updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); assertEquals(atoms, result); - result = CoreHelper.updateValueByPointer(atoms, "/nucleas/NumberOfProtons", v -> NUMBER_OF_NEUTRONS); + result = CoreHelper + .updateValueByPointer(atoms, "/nucleas/NumberOfProtons", + v -> NUMBER_OF_NEUTRONS); assertEquals(atoms, result); - result = CoreHelper.updateValueByPointer(atoms, "/atom", v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); + result = CoreHelper + .updateValueByPointer(atoms, "/atom", + v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); assertEquals(atoms, result); } From e9851432bd480fc764ed36b4fa8751fd4dad862e Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 15:12:46 +0500 Subject: [PATCH 26/33] test(pagination): fix linting issues(3) --- src/test/java/apimatic/core/EndToEndTest.java | 8 ++++- .../apimatic/core/RequestBuilderTest.java | 29 ++++++++++--------- .../type/pagination/CursorPaginationTest.java | 18 +++++++----- .../type/pagination/PagePaginationTest.java | 4 +-- .../type/pagination/PaginatedDataTest.java | 2 +- 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index fb40cca6..d0d974b0 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -129,6 +129,12 @@ public class EndToEndTest extends MockCoreConfig { * Subclasses can override this method to provide a custom response retrieval mechanism. * If overridden, ensure thread-safety and proper response management. */ + + /** + * Returns the current Response object. + * + * @return the response instance + */ protected Response getResponse() { return response; } @@ -136,7 +142,7 @@ protected Response getResponse() { /** * Allows subclasses to customize how the response is set. *

- * If overridden, ensure that the response is correctly handled and does not introduce memory leaks. + * Ensure that the response is correctly handled and does not introduce memory leaks. * * @param response The response to set. */ diff --git a/src/test/java/apimatic/core/RequestBuilderTest.java b/src/test/java/apimatic/core/RequestBuilderTest.java index 70640672..1e09a7b4 100644 --- a/src/test/java/apimatic/core/RequestBuilderTest.java +++ b/src/test/java/apimatic/core/RequestBuilderTest.java @@ -174,10 +174,10 @@ public void testUpdateSimpleFormParam() throws IOException { public void testUpdateComplexFormParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .formParam(param -> param.key("form") - .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); updateAndVerify(localRequestBuilder, "$request.body#/form/NumberOfElectrons", - ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -192,10 +192,10 @@ public void testUpdateSimpleQueryParam() throws IOException { public void testUpdateComplexQueryParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .queryParam(param -> param.key("que") - .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); updateAndVerify(localRequestBuilder, "$request.query#/que/NumberOfElectrons", - ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -203,17 +203,18 @@ public void testUpdateSimpleHeaderParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .headerParam(param -> param.key("head").value(HEADER_DEFAULT)); - updateAndVerify(localRequestBuilder, "$request.headers#/head", HEADER_DEFAULT, HEADER_UPDATED); + updateAndVerify(localRequestBuilder, "$request.headers#/head", + HEADER_DEFAULT, HEADER_UPDATED); } @Test public void testUpdateComplexHeaderParam() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.GET) .headerParam(param -> param.key("head") - .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); + .value(new Atom(ELECTRONS_DEFAULT, ELECTRONS_DEFAULT))); updateAndVerify(localRequestBuilder, "$request.headers#/head/NumberOfElectrons", - ELECTRONS_DEFAULT, ELECTRONS_UPDATED); + ELECTRONS_DEFAULT, ELECTRONS_UPDATED); } @Test @@ -231,19 +232,19 @@ public void testUpdateComplexBodyParam() throws IOException { .bodyParam(param -> param.value(new Atom(ATOM_NUMBER, ATOM_MASS))); updateAndVerify(localRequestBuilder, "$request.body#/NumberOfElectrons", - ATOM_NUMBER, ATOM_MASS); + ATOM_NUMBER, ATOM_MASS); } @Test public void testUpdateMultipleComplexBodyParams() throws IOException { HttpRequest.Builder localRequestBuilder = new HttpRequest.Builder().httpMethod(Method.POST) .bodyParam(param -> param.key("bodyA") - .value(new Atom(BODY_A_ELECTRONS, BODY_A_MASS))) + .value(new Atom(BODY_A_ELECTRONS, BODY_A_MASS))) .bodyParam(param -> param.key("bodyB") - .value(new Atom(BODY_B_ELECTRONS, BODY_B_MASS))); + .value(new Atom(BODY_B_ELECTRONS, BODY_B_MASS))); updateAndVerify(localRequestBuilder, "$request.body#/bodyB/NumberOfElectrons", - BODY_B_ELECTRONS, BODY_B_UPDATED); + BODY_B_ELECTRONS, BODY_B_UPDATED); } @Test @@ -255,7 +256,7 @@ public void testUpdateSimpleBodyParam() throws IOException { } private void updateAndVerify(HttpRequest.Builder requestBuilder, - String pointer, Object oldValue, Object newValue) { + String pointer, Object oldValue, Object newValue) { requestBuilder.updateParameterByJsonPointer(pointer, old -> { assertEquals(oldValue.toString(), old.toString()); return newValue; @@ -273,8 +274,8 @@ private void updateAndVerify(HttpRequest.Builder requestBuilder, public void testBodyParamValidation() throws IOException { // when new HttpRequest.Builder() - .httpMethod(Method.POST) - .bodyParam(param -> param.value(null)) + .httpMethod(Method.POST) + .bodyParam(param -> param.value(null)) .build(getMockGlobalConfig()); } diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index dc704a3b..aae02a7b 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -20,6 +20,10 @@ public class CursorPaginationTest { + /** + * Initializes Mockito rule to enable Mockito annotations and + * silently handle unnecessary stubbings during tests. + */ @Rule public MockitoRule initRule = MockitoJUnit.rule().silent(); @@ -43,7 +47,7 @@ public void testWithValidCursor() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_QUERY_POINTER); + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -67,7 +71,7 @@ public void testWithValidCursorAndDifferentTypeA() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_QUERY_POINTER); + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -92,7 +96,7 @@ public void testWithValidCursorAndDifferentType() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_QUERY_POINTER); + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -115,7 +119,7 @@ public void testWithValidCursorButMissingInFirstRequest() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_QUERY_POINTER); + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); @@ -132,7 +136,7 @@ public void testWithValidCursorFromResponseBody() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_INT); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_QUERY_POINTER); + REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNotNull(requestBuilder); @@ -156,7 +160,7 @@ public void testWithInvalidResponsePointer() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_INVALID, - REQUEST_HEADER_POINTER); + REQUEST_HEADER_POINTER); Builder requestBuilder = cursor.apply(paginatedData); assertNull(requestBuilder); @@ -189,7 +193,7 @@ public void testWithInvalidRequestPointer() { when(response.getBody()).thenReturn(NEXT_CURSOR_JSON_STRING); CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, - REQUEST_POINTER_INVALID); + REQUEST_POINTER_INVALID); assertNull(cursor.apply(paginatedData)); } diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 242a81d3..40f60df5 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -174,8 +174,8 @@ public void testWithInvalidStringPage() { when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder() - .queryParam(q -> q.key("page") - .value(INVALID_PAGE_STRING))); + .queryParam(q -> q.key("page") + .value(INVALID_PAGE_STRING))); when(paginatedData.getResponse()).thenReturn(response); when(response.getBody()).thenReturn("{\"page\": \"" + INVALID_PAGE_STRING + "\"}"); diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 22eb1784..ef1225d5 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -74,7 +74,7 @@ public void testInvalidPaginationWithFailingResponse() throws IOException { public void testInvalidPaginationWithFailingResponseAsync() throws IOException, InterruptedException, ExecutionException { Runnable call1 = () -> when(getResponse() - .getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); + .getStatusCode()).thenReturn(NOT_FOUND_STATUS_CODE); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, new LinkPagination("$response.body#/next_link")); From 1225158d67a70ca990f1ccb2cc28ae683955661c Mon Sep 17 00:00:00 2001 From: "M.Hamza Shahzad" Date: Thu, 12 Jun 2025 15:19:25 +0500 Subject: [PATCH 27/33] test(pagination): fix linting issues(4) --- src/test/java/apimatic/core/EndToEndTest.java | 6 ------ .../core/type/pagination/CursorPaginationTest.java | 2 +- .../java/apimatic/core/utilities/CoreHelperTest.java | 10 +++++----- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/test/java/apimatic/core/EndToEndTest.java b/src/test/java/apimatic/core/EndToEndTest.java index d0d974b0..3202b147 100644 --- a/src/test/java/apimatic/core/EndToEndTest.java +++ b/src/test/java/apimatic/core/EndToEndTest.java @@ -123,12 +123,6 @@ public class EndToEndTest extends MockCoreConfig { */ @Mock private Response response; - /** - * Base test class for end-to-end tests. - *

- * Subclasses can override this method to provide a custom response retrieval mechanism. - * If overridden, ensure thread-safety and proper response management. - */ /** * Returns the current Response object. diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index aae02a7b..6194fbce 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -21,7 +21,7 @@ public class CursorPaginationTest { /** - * Initializes Mockito rule to enable Mockito annotations and + * Initializes Mockito rule to enable Mockito annotations and * silently handle unnecessary stubbings during tests. */ @Rule diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index 442cfc61..ede7b802 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -1860,17 +1860,17 @@ public void testUpdateValueByPointerInvalidCases() { atoms.put("nucleas", new Atom(NUMBER_OF_PROTONS, null)); Map result = CoreHelper - .updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); + .updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); assertEquals(atoms, result); result = CoreHelper - .updateValueByPointer(atoms, "/nucleas/NumberOfProtons", - v -> NUMBER_OF_NEUTRONS); + .updateValueByPointer(atoms, "/nucleas/NumberOfProtons", + v -> NUMBER_OF_NEUTRONS); assertEquals(atoms, result); result = CoreHelper - .updateValueByPointer(atoms, "/atom", - v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); + .updateValueByPointer(atoms, "/atom", + v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); assertEquals(atoms, result); } From cee79a64d4f133334dcacfefe4f794f66156d4a6 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Thu, 12 Jun 2025 18:06:40 +0500 Subject: [PATCH 28/33] re used copy function --- .../io/apimatic/core/types/pagination/PaginatedData.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 61dad642..1465db07 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -100,8 +100,7 @@ public T getPage(Function, T> pageSupplier) { * @return An Iterator of all items of type T. */ public Iterator items(Function, T> itemSupplier) { - PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, pageCreator, itemsCreator, strategies); + PaginatedData paginatedData = copy(); return new Iterator() { @Override @@ -130,8 +129,7 @@ public T next() { * @return An Iterator of all pages of type T. */ public Iterator pages(Function, T> pageSupplier) { - PaginatedData paginatedData = new PaginatedData<>( - firstApiCall, pageCreator, itemsCreator, strategies); + PaginatedData paginatedData = copy(); return new Iterator() { @Override From 4f005d18f6b4a9726d55ef1122bf032f6c537db5 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Fri, 13 Jun 2025 15:18:19 +0500 Subject: [PATCH 29/33] add suggested changes to PR --- .../java/io/apimatic/core/HttpRequest.java | 14 +- .../types/pagination/CursorPagination.java | 3 +- .../core/types/pagination/LinkPagination.java | 3 +- .../types/pagination/OffsetPagination.java | 6 +- .../core/types/pagination/PagePagination.java | 6 +- .../core/types/pagination/PageWrapper.java | 99 ++++++--- .../core/types/pagination/PaginatedData.java | 3 +- .../apimatic/core/utilities/CoreHelper.java | 6 +- .../type/pagination/CursorPaginationTest.java | 58 ++++- .../type/pagination/LinkPaginationTest.java | 22 +- .../type/pagination/OffsetPaginationTest.java | 56 ++--- .../type/pagination/PagePaginationTest.java | 53 +++-- .../type/pagination/PaginatedDataTest.java | 208 +++++++++--------- .../core/utilities/CoreHelperTest.java | 5 - 14 files changed, 315 insertions(+), 227 deletions(-) diff --git a/src/main/java/io/apimatic/core/HttpRequest.java b/src/main/java/io/apimatic/core/HttpRequest.java index 9f5d88d9..acc71b87 100644 --- a/src/main/java/io/apimatic/core/HttpRequest.java +++ b/src/main/java/io/apimatic/core/HttpRequest.java @@ -381,6 +381,7 @@ private void updateBodyParams(UnaryOperator setter, String point) { } body = CoreHelper.updateValueByPointer(body, point, setter); + return; } if (bodyParameters != null) { @@ -634,19 +635,6 @@ private static String getSerializedHeaderValue(Object obj) { return CoreHelper.trySerialize(obj); } - /** - * @return A query URL combining the path, query and template parameters. - */ - public String getQueryUrl() { - StringBuilder builder = new StringBuilder(path); - - CoreHelper.appendUrlWithQueryParameters(builder, queryParams, - arraySerializationFormat); - CoreHelper.appendUrlWithTemplateParameters(builder, templateParams); - - return builder.toString(); - } - /** * @return A copy of this request builder instance. */ diff --git a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java index 8e334af7..4fe525ff 100644 --- a/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/CursorPagination.java @@ -25,9 +25,9 @@ public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); AtomicBoolean isUpdated = new AtomicBoolean(false); + currentRequestCursor = null; reqBuilder.updateParameterByJsonPointer(input, old -> { - if (response == null) { currentRequestCursor = (String) old; isUpdated.set(true); @@ -55,6 +55,5 @@ public Builder apply(PaginatedData paginatedData) { @Override public void addMetaData(PageWrapper page) { page.setCursorInput(currentRequestCursor); - currentRequestCursor = null; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java index 6978a622..cca1ad28 100644 --- a/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/LinkPagination.java @@ -18,9 +18,9 @@ public LinkPagination(final String next) { @Override public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); + currentRequestLink = null; if (response == null) { - currentRequestLink = paginatedData.getRequestBuilder().getQueryUrl(); return paginatedData.getRequestBuilder(); } @@ -38,6 +38,5 @@ public Builder apply(PaginatedData paginatedData) { @Override public void addMetaData(PageWrapper page) { page.setNextLinkInput(currentRequestLink); - currentRequestLink = null; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java index 19de2f17..aef7cf3f 100644 --- a/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/OffsetPagination.java @@ -7,7 +7,7 @@ public class OffsetPagination implements PaginationStrategy { private final String input; - private int currentRequestOffset = -1; + private int currentRequestOffset; /** * @param input JsonPointer of a field in request, representing offset. @@ -21,9 +21,10 @@ public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); AtomicBoolean isUpdated = new AtomicBoolean(false); + currentRequestOffset = 0; reqBuilder.updateParameterByJsonPointer(input, old -> { - int oldValue = Integer.parseInt("" + old); + int oldValue = old == null ? 0 : Integer.parseInt("" + old); if (response == null) { currentRequestOffset = oldValue; @@ -47,6 +48,5 @@ public Builder apply(PaginatedData paginatedData) { @Override public void addMetaData(PageWrapper page) { page.setOffsetInput(currentRequestOffset); - currentRequestOffset = -1; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java index 042292a2..33b80dc8 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PagePagination.java +++ b/src/main/java/io/apimatic/core/types/pagination/PagePagination.java @@ -7,7 +7,7 @@ public class PagePagination implements PaginationStrategy { private final String input; - private int currentRequestPageNumber = -1; + private int currentRequestPageNumber; /** * @param input JsonPointer of a field in request, representing page. @@ -21,9 +21,10 @@ public Builder apply(PaginatedData paginatedData) { Response response = paginatedData.getResponse(); Builder reqBuilder = paginatedData.getRequestBuilder(); AtomicBoolean isUpdated = new AtomicBoolean(false); + currentRequestPageNumber = 0; reqBuilder.updateParameterByJsonPointer(input, old -> { - int oldValue = Integer.parseInt("" + old); + int oldValue = old == null ? 1 : Integer.parseInt("" + old); if (response == null) { currentRequestPageNumber = oldValue; @@ -47,6 +48,5 @@ public Builder apply(PaginatedData paginatedData) { @Override public void addMetaData(PageWrapper page) { page.setPageInput(currentRequestPageNumber); - currentRequestPageNumber = -1; } } diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 837600c6..87befc41 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -17,42 +17,67 @@ public final class PageWrapper implements ApiResponseType

{ * @param items Extracted items from the page. * @return An new instance of PageWrapper. */ - public static PageWrapper create(Response response, P page, List items) { - return new PageWrapper(response.getStatusCode(), response.getHeaders(), page, items); + public static PageWrapper create(Response response, P page, + List items, PaginationStrategy strategy) { + return new PageWrapper(response.getStatusCode(), response.getHeaders(), page, + items, strategy); } private final int statusCode; private final HttpHeaders headers; - private P page; - private List items; + private final P page; + private final List items; + private final PaginationStrategy strategy; - private String nextLinkInput = null; - private int offsetInput = -1; - private int pageInput = -1; - private String cursorInput = null; + private String nextLinkInput; + private int offsetInput; + private int pageInput; + private String cursorInput; private PageWrapper(int statusCode, final HttpHeaders headers, final P page, - final List items) { + final List items, final PaginationStrategy strategy) { this.statusCode = statusCode; this.headers = headers; this.page = page; this.items = items; + this.strategy = strategy; + strategy.addMetaData(this); } - public void setNextLinkInput(String nextLinkInput) { - this.nextLinkInput = nextLinkInput; + /** + * @return True if cursor paginated page is wrapped + */ + public boolean isCursorPagination() { + return strategy instanceof CursorPagination; } - public void setOffsetInput(int offsetInput) { - this.offsetInput = offsetInput; + /** + * @return True if link paginated page is wrapped + */ + public boolean isLinkPagination() { + return strategy instanceof LinkPagination; } - public void setPageInput(int pageInput) { - this.pageInput = pageInput; + /** + * @return True if offset paginated page is wrapped + */ + public boolean isOffsetPagination() { + return strategy instanceof OffsetPagination; } - public void setCursorInput(String cursorInput) { - this.cursorInput = cursorInput; + /** + * @return True if number paginated page is wrapped + */ + public boolean isNumberPagination() { + return strategy instanceof PagePagination; + } + + /** + * Sets the next link input + * @param strategy value of next link input + */ + public void setNextLinkInput(String nextLinkInput) { + this.nextLinkInput = nextLinkInput; } /** @@ -63,6 +88,30 @@ public String getNextLinkInput() { return nextLinkInput; } + /** + * Sets the cursor input + * @param strategy value of cursor input + */ + public void setCursorInput(String cursorInput) { + this.cursorInput = cursorInput; + } + + /** + * Gets the cursor input used for cursor-based pagination. + * @return The cursor token + */ + public String getCursorInput() { + return cursorInput; + } + + /** + * Sets the offset input + * @param strategy value of offset input + */ + public void setOffsetInput(int offsetInput) { + this.offsetInput = offsetInput; + } + /** * Gets the offset input used for offset-based pagination. * @return The offset value @@ -72,19 +121,19 @@ public int getOffsetInput() { } /** - * Gets the page number input used for page-based pagination. - * @return The page number + * Sets the page input + * @param strategy value of page input */ - public int getPageInput() { - return pageInput; + public void setPageInput(int pageInput) { + this.pageInput = pageInput; } /** - * Gets the cursor input used for cursor-based pagination. - * @return The cursor token + * Gets the page number input used for page-based pagination. + * @return The page number */ - public String getCursorInput() { - return cursorInput; + public int getPageInput() { + return pageInput; } /** diff --git a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java index 1465db07..9c1d50a9 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java +++ b/src/main/java/io/apimatic/core/types/pagination/PaginatedData.java @@ -240,8 +240,7 @@ private boolean updateWith(ApiCall apiCall, R pageUnWrapped, this.apiCall = apiCall; PageWrapper pageWrapper = PageWrapper.create(apiCall.getResponse(), pageUnWrapped, - itemsUnWrapped); - strategy.addMetaData(pageWrapper); + itemsUnWrapped, strategy); this.page = CheckedSupplier.create(pageCreator.apply(pageWrapper)); itemsUnWrapped.forEach(i -> items.add(CheckedSupplier.create(i))); diff --git a/src/main/java/io/apimatic/core/utilities/CoreHelper.java b/src/main/java/io/apimatic/core/utilities/CoreHelper.java index d655763c..fece702d 100644 --- a/src/main/java/io/apimatic/core/utilities/CoreHelper.java +++ b/src/main/java/io/apimatic/core/utilities/CoreHelper.java @@ -1708,10 +1708,10 @@ public static T updateValueByPointer(T value, String pointer, } JsonPointer jsonPointer = Json.createPointer(pointer); + if (!jsonPointer.containsValue(structure)) { - return value; + structure = jsonPointer.add(structure, JsonValue.NULL); } - JsonValue oldJsonValue = jsonPointer.getValue(structure); Object oldValue = toObject(oldJsonValue); Object newValueRaw = updater.apply(oldValue); @@ -1755,7 +1755,7 @@ private static Object toObject(JsonValue value) { case FALSE: return false; default: - return value.toString(); + return null; } } diff --git a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java index 6194fbce..1f0649af 100644 --- a/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/CursorPaginationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -55,8 +56,8 @@ public void testWithValidCursor() { assertEquals("xyz123", v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - cursor.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); assertEquals("xyz123", pageWrapper.getCursorInput()); } @@ -79,8 +80,8 @@ public void testWithValidCursorAndDifferentTypeA() { assertEquals("123", v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - cursor.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); assertEquals("123", pageWrapper.getCursorInput()); } @@ -104,8 +105,8 @@ public void testWithValidCursorAndDifferentType() { assertEquals("123", v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - cursor.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); assertEquals("123", pageWrapper.getCursorInput()); } @@ -122,7 +123,36 @@ public void testWithValidCursorButMissingInFirstRequest() { REQUEST_QUERY_POINTER); Builder requestBuilder = cursor.apply(paginatedData); - assertNull(requestBuilder); + assertNotNull(requestBuilder); + requestBuilder.updateParameterByJsonPointer(RESPONSE_POINTER_VALID, v -> { + assertEquals(null, v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); + assertEquals("xyz123", pageWrapper.getCursorInput()); + } + + @Test + public void testWithMissingCursorFirstRequest() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(null); + + CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, + REQUEST_QUERY_POINTER); + + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); + requestBuilder.updateParameterByJsonPointer(RESPONSE_POINTER_VALID, v -> { + assertEquals(null, v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); + assertEquals(null, pageWrapper.getCursorInput()); } @Test @@ -144,8 +174,8 @@ public void testWithValidCursorFromResponseBody() { assertEquals("123", v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - cursor.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); assertEquals("123", pageWrapper.getCursorInput()); } @@ -195,7 +225,15 @@ public void testWithInvalidRequestPointer() { CursorPagination cursor = new CursorPagination(RESPONSE_POINTER_VALID, REQUEST_POINTER_INVALID); - assertNull(cursor.apply(paginatedData)); + Builder requestBuilder = cursor.apply(paginatedData); + assertNotNull(requestBuilder); + requestBuilder.updateParameterByJsonPointer(REQUEST_POINTER_INVALID, v -> { + assertEquals("xyz123", v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, cursor); + assertTrue(pageWrapper.isCursorPagination()); + assertEquals("xyz123", pageWrapper.getCursorInput()); } @Test diff --git a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java index fb8c66e1..58c434d8 100644 --- a/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/LinkPaginationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -40,7 +41,6 @@ public class LinkPaginationTest { private static final String SIZE = "size"; private static final String NEXT = "next"; - /** * Silent rule for Mockito initialization. */ @@ -65,8 +65,8 @@ public void testValidLink() { return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - link.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, link); + assertTrue(pageWrapper.isLinkPagination()); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -103,8 +103,8 @@ public void testValidLinkWithAdditionalParams() { return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - link.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, link); + assertTrue(pageWrapper.isLinkPagination()); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -130,8 +130,8 @@ public void testValidLinkFromHeader() { return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - link.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, link); + assertTrue(pageWrapper.isLinkPagination()); assertEquals(NEXT_URL_SINGLE, pageWrapper.getNextLinkInput()); } @@ -201,8 +201,8 @@ public void testMultipleQueryParams() { return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - link.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, link); + assertTrue(pageWrapper.isLinkPagination()); assertEquals(NEXT_URL_MULTIPLE, pageWrapper.getNextLinkInput()); } @@ -230,8 +230,8 @@ public void testEncodedQueryParams() { return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - link.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, link); + assertTrue(pageWrapper.isLinkPagination()); assertEquals(NEXT_URL_ENCODED, pageWrapper.getNextLinkInput()); } } diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 05fb8858..95fdbb5b 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,7 +37,7 @@ public class OffsetPaginationTest { private static final int OFFSET_STRING = 5; private static final int OFFSET_STRING_PLUS_PAGE = OFFSET_STRING + PAGE_SIZE; private static final int NUMERIC_OFFSET = 5; - private static final int INVALID_OFFSET_RESULT = -1; + private static final int INVALID_OFFSET_RESULT = 0; private static final String INVALID_OFFSET_STRING = "5a"; /** @@ -66,8 +67,8 @@ public void testValidOffsetHeader() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -130,8 +131,8 @@ public void testValidOffsetWithBodySerializer() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -151,8 +152,8 @@ public void testValidOffsetSingleBodyParamIntType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -172,8 +173,8 @@ public void testValidOffsetSingleBodyParamStringType() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -195,8 +196,8 @@ public void testValidOffsetSingleBodyParam() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -218,8 +219,8 @@ public void testValidOffsetMultipleBodyParams() { HttpRequest.Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -243,9 +244,8 @@ public void testValidOffsetTemplate() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -268,8 +268,8 @@ public void testValidOffset() { assertEquals(OFFSET_PLUS_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -297,8 +297,8 @@ public void testValidOffsetAsInnerField() { assertEquals(OFFSET_VAL_PLUS_ONE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_VAL_PLUS_ONE, pageWrapper.getOffsetInput()); } @@ -321,8 +321,8 @@ public void testValidStringOffset() { assertEquals(OFFSET_STRING_PLUS_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(OFFSET_STRING_PLUS_PAGE, pageWrapper.getOffsetInput()); } @@ -344,8 +344,8 @@ public void testInvalidStringOffset() { assertEquals(INVALID_OFFSET_STRING, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @@ -360,8 +360,8 @@ public void testMissingOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @@ -383,8 +383,8 @@ public void testNullOffset() { assertEquals(NUMERIC_OFFSET, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - offset.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); + assertTrue(pageWrapper.isOffsetPagination()); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } } diff --git a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java index 40f60df5..1f50db5a 100644 --- a/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/PagePaginationTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -59,8 +60,8 @@ public void testWithValidPageHeader() { assertEquals(NEXT_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - page.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -83,8 +84,8 @@ public void testWithValidPageTemplate() { assertEquals(NEXT_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - page.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -107,8 +108,8 @@ public void testWithValidPage() { assertEquals(NEXT_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - page.addMetaData(pageWrapper); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); assertEquals(NEXT_PAGE, pageWrapper.getPageInput()); } @@ -136,9 +137,8 @@ public void testWithValidPageAsInnerField() { assertEquals(INNER_FIELD_NEXT_PAGE, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - page.addMetaData(pageWrapper); - + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); assertEquals(INNER_FIELD_NEXT_PAGE, pageWrapper.getPageInput()); } @@ -161,9 +161,8 @@ public void testWithValidStringPage() { assertEquals(NEXT_PAGE_FROM_STRING, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(response, null, null); - page.addMetaData(pageWrapper); - + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); assertEquals(NEXT_PAGE_FROM_STRING, pageWrapper.getPageInput()); } @@ -185,6 +184,27 @@ public void testWithInvalidStringPage() { assertNull(requestBuilder); } + @Test + public void testWithMissingPageFirstCall() { + PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); + + when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); + when(paginatedData.getResponse()).thenReturn(null); + + PagePagination page = new PagePagination("$request.query#/page"); + + Builder requestBuilder = page.apply(paginatedData); + assertNotNull(requestBuilder); + requestBuilder.updateParameterByJsonPointer("$request.query#/page", v -> { + assertEquals(null, v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); + assertEquals(INNER_FIELD_PAGE, pageWrapper.getPageInput()); + } + @Test public void testWithMissingPage() { PaginatedData paginatedData = mock(PaginatedData.class); @@ -197,7 +217,14 @@ public void testWithMissingPage() { PagePagination page = new PagePagination("$request.query#/page"); Builder requestBuilder = page.apply(paginatedData); - assertNull(requestBuilder); + assertNotNull(requestBuilder); + requestBuilder.updateParameterByJsonPointer("$request.query#/page", v -> { + assertEquals(INNER_FIELD_NEXT_PAGE, v); + return v; + }); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, page); + assertTrue(pageWrapper.isNumberPagination()); + assertEquals(INNER_FIELD_NEXT_PAGE, pageWrapper.getPageInput()); } @Test diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index ef1225d5..069dc37b 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -17,7 +18,6 @@ import java.util.function.Function; import org.junit.Test; -import org.junit.runner.RunWith; import apimatic.core.EndToEndTest; import io.apimatic.core.ApiCall; @@ -38,10 +38,6 @@ import io.apimatic.coreinterfaces.http.request.Request; import io.apimatic.coreinterfaces.http.request.configuration.RetryOption; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -@RunWith(Parameterized.class) public class PaginatedDataTest extends EndToEndTest { private static final int NOT_FOUND_STATUS_CODE = 404; private static final int SUCCESS_STATUS_CODE = 200; @@ -89,36 +85,24 @@ public void testInvalidPaginationWithFailingResponseAsync() assertFalse(hasNext); } - /** - * Provides invalid pagination test cases to verify handling of edge cases and null responses. - * - * @return A list of invalid pagination test cases, each represented as an object array. - */ - @Parameters - public static List invalidPaginationTestCases() { - return Arrays.asList(new Object[][] { - {"{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"}, - {"{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"}, - {null} - }); + @Test + public void testInvalidPaginationWithEmptyItems() throws IOException { + assertInvalidPaginatedData( + "{\"data\":[],\"next_link\":\"https://localhost:3000/path?page=2\"}"); } - private final String responseBody; - - /** - * Constructs a PaginatedDataTest with the given response body. - * - * @param responseBody the response body string to be used in the test - */ - public PaginatedDataTest(final String responseBody) { - this.responseBody = responseBody; + @Test + public void testInvalidPaginationWithNullItems() throws IOException { + assertInvalidPaginatedData( + "{\"data\":null,\"next_link\":\"https://localhost:3000/path?page=2\"}"); } - /** - * Tests invalid pagination scenarios - */ @Test - public void testInvalidPagination() throws IOException { + public void testInvalidPaginationWithNullPage() throws IOException { + assertInvalidPaginatedData(null); + } + + private void assertInvalidPaginatedData(String responseBody) throws IOException { Runnable call1 = () -> when(getResponse().getBody()).thenReturn(responseBody); PaginatedData, RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, @@ -139,8 +123,13 @@ public void testLinkPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - verifyData(getPaginatedData(call1, call2, call3, - new LinkPagination("$response.body#/next_link"))); + List> pages = verifyData(getPaginatedData(call1, call2, + call3, new LinkPagination("$response.body#/next_link"))); + + assertTrue(pages.get(0).isLinkPagination()); + assertEquals(null, pages.get(0).getNextLinkInput()); + assertTrue(pages.get(1).isLinkPagination()); + assertEquals("https://localhost:3000/path?page=2", pages.get(1).getNextLinkInput()); } @Test @@ -172,9 +161,14 @@ public void testCursorPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - verifyData(getPaginatedData(call1, call2, call3, + List> pages = verifyData(getPaginatedData(call1, call2, call3, new CursorPagination("$response.body#/page_info", "$request.path#/cursor"))); + + assertTrue(pages.get(0).isCursorPagination()); + assertEquals("cursor", pages.get(0).getCursorInput()); + assertTrue(pages.get(1).isCursorPagination()); + assertEquals("fruits", pages.get(1).getCursorInput()); } @Test @@ -214,8 +208,13 @@ public void testOffsetPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - verifyData(getPaginatedData(call1, call2, call3, + List> pages = verifyData(getPaginatedData(call1, call2, call3, new OffsetPagination("$request.headers#/offset"))); + + assertTrue(pages.get(0).isOffsetPagination()); + assertEquals(0, pages.get(0).getOffsetInput()); + assertTrue(pages.get(1).isOffsetPagination()); + assertEquals(3, pages.get(1).getOffsetInput()); } @Test @@ -247,8 +246,13 @@ public void testPagePaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - verifyData(getPaginatedData(call1, call2, call3, + List> pages = verifyData(getPaginatedData(call1, call2, call3, new PagePagination("$request.query#/page"))); + + assertTrue(pages.get(0).isNumberPagination()); + assertEquals(1, pages.get(0).getPageInput()); + assertTrue(pages.get(1).isNumberPagination()); + assertEquals(2, pages.get(1).getPageInput()); } @Test @@ -257,14 +261,14 @@ public void testInvalidPagePaginationData() throws IOException, CoreApiException "{\"data\":[\"apple\",\"mango\",\"orange\"]}"); PaginatedData, - RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, null, null, + RecordPage, CoreApiException> paginatedData = getPaginatedData(call1, call1, null, new PagePagination("$request.query#/INVALID")); Iterator, CoreApiException>> pages = paginatedData.pages(cs -> cs); assertTrue(pages.hasNext()); assertTrue(pages.hasNext()); assertEquals(Arrays.asList("apple", "mango", "orange"), pages.next().get().getItems()); - assertFalse(pages.hasNext()); + assertTrue(pages.hasNext()); } @Test @@ -280,9 +284,14 @@ public void testMultiPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - verifyData(getPaginatedData(call1, call2, call3, + List> pages = verifyData(getPaginatedData(call1, call2, call3, new LinkPagination("$response.INVALID#/next_link"), new PagePagination("$request.body#/limit"))); + + assertTrue(pages.get(0).isLinkPagination()); + assertEquals(null, pages.get(0).getNextLinkInput()); + assertTrue(pages.get(1).isNumberPagination()); + assertEquals(2, pages.get(1).getPageInput()); } @Test @@ -338,10 +347,6 @@ public void testMultiPaginationDataAsync() } }; - RecordPage expectedPage1 = new RecordPage(); - expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); - expectedPage1.setPageInfo("fruits"); - RecordPage expectedPage2 = new RecordPage(); expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); expectedPage2.setPageInfo("vegitables"); @@ -353,22 +358,23 @@ public void testMultiPaginationDataAsync() boolean hasNext = paginatedData.fetchNextPageAsync().get(); assertEquals(true, hasNext); - assertEquals(expectedPage1.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); - assertEquals(expectedPage1.getData(), page.getResult().getData()); - assertEquals(expectedPage1.getNextLink(), page.getResult().getNextLink()); - assertEquals(expectedPage1.getPageInfo(), page.getResult().getPageInfo()); - assertEquals("/path/cursor?page=1", page.getNextLinkInput()); - assertEquals(-1, page.getPageInput()); + assertEquals("fruits", page.getResult().getPageInfo()); + assertEquals(Arrays.asList("apple", "mango", "orange"), page.getItems()); + assertEquals(page.getItems(), page.getResult().getData()); + assertEquals(page.getItems(), paginatedData.getItems(itemCreator)); + assertTrue(page.isLinkPagination()); + assertEquals(null, page.getNextLinkInput()); hasNext = paginatedData.fetchNextPageAsync().get(); assertEquals(true, hasNext); - assertEquals(expectedPage2.getData(), paginatedData.getItems(itemCreator)); page = paginatedData.getPage(pageCreator); - assertEquals(expectedPage2.getData(), page.getResult().getData()); - assertEquals(expectedPage2.getNextLink(), page.getResult().getNextLink()); - assertEquals(expectedPage2.getPageInfo(), page.getResult().getPageInfo()); - assertNull(page.getNextLinkInput()); + assertEquals("vegitables", page.getResult().getPageInfo()); + assertEquals("https://localhost:3000/path?page=3", page.getResult().getNextLink()); + assertEquals(Arrays.asList("potato", "carrot", "tomato"), page.getItems()); + assertEquals(page.getItems(), page.getResult().getData()); + assertEquals(page.getItems(), paginatedData.getItems(itemCreator)); + assertTrue(page.isNumberPagination()); assertEquals(2, page.getPageInput()); hasNext = paginatedData.fetchNextPageAsync().get(); @@ -378,73 +384,61 @@ public void testMultiPaginationDataAsync() assertEquals(0, paginatedData.getItems(itemCreator).size()); } - private void verifyData(PaginatedData, + private List> verifyData(PaginatedData, RecordPage, CoreApiException> paginatedData) throws CoreApiException, IOException { - int index = 0; - List expectedItems = Arrays.asList( - "apple", "mango", "orange", "potato", "carrot", "tomato"); - Iterator> itemIterator = paginatedData.items(cs -> cs); - while (itemIterator.hasNext()) { - String d = itemIterator.next().get(); - assertNotNull(d); - assertEquals(expectedItems.get(index), d); - index++; - } + + assertTrue(itemIterator.hasNext()); + assertEquals("apple", itemIterator.next().get()); + assertTrue(itemIterator.hasNext()); + assertEquals("mango", itemIterator.next().get()); + assertTrue(itemIterator.hasNext()); + assertEquals("orange", itemIterator.next().get()); + assertTrue(itemIterator.hasNext()); + assertEquals("potato", itemIterator.next().get()); + assertTrue(itemIterator.hasNext()); + assertEquals("carrot", itemIterator.next().get()); + assertTrue(itemIterator.hasNext()); + assertEquals("tomato", itemIterator.next().get()); + assertFalse(itemIterator.hasNext()); Exception exception = assertThrows(NoSuchElementException.class, itemIterator::next); assertEquals("No more items available.", exception.getMessage()); - RecordPage expectedPage1 = new RecordPage(); - expectedPage1.setData(Arrays.asList("apple", "mango", "orange")); - expectedPage1.setPageInfo("fruits"); - expectedPage1.setNextLink("https://localhost:3000/path?page=2"); - - RecordPage expectedPage2 = new RecordPage(); - expectedPage2.setData(Arrays.asList("potato", "carrot", "tomato")); - expectedPage2.setPageInfo("vegitables"); - expectedPage2.setNextLink("https://localhost:3000/path?page=3"); - - int pageOffset = 0; - List expectedPages = Arrays.asList(expectedPage1, expectedPage2); + List> pages = new ArrayList<>(); + Iterator, CoreApiException>> pagesIterator = paginatedData.pages(cs -> cs); - while (pagesIterator.hasNext()) { - PageWrapper p = pagesIterator.next().get(); - assertNotNull(p); - - String expectedPageInfo = expectedPages.get(pageOffset).getPageInfo(); - String expectedNextLink = expectedPages.get(pageOffset).getNextLink(); - List expectedData = expectedPages.get(pageOffset).getData(); - - assertEquals(expectedPageInfo, p.getResult().getPageInfo()); - assertEquals(expectedNextLink, p.getResult().getNextLink()); - assertEquals(expectedData, p.getResult().getData()); - assertEquals(expectedData, p.getItems()); - assertEquals(SUCCESS_STATUS_CODE, p.getStatusCode()); - assertEquals(getHttpHeaders(), p.getHeaders()); - if (p.getCursorInput() != null && pageOffset > 0) { - assertEquals(expectedPages.get(pageOffset - 1).getPageInfo(), p.getCursorInput()); - } - - if (p.getNextLinkInput() != null && pageOffset > 0) { - assertEquals(expectedPages.get(pageOffset - 1).getNextLink(), p.getNextLinkInput()); - } - - if (p.getPageInput() != -1) { - assertEquals(pageOffset + 1, p.getPageInput()); - } - - if (p.getOffsetInput() != -1) { - assertEquals(pageOffset * PAGE_SIZE, p.getOffsetInput()); - } - pageOffset++; - } + assertTrue(pagesIterator.hasNext()); + PageWrapper p = pagesIterator.next().get(); + assertNotNull(p); + assertEquals("fruits", p.getResult().getPageInfo()); + assertEquals("https://localhost:3000/path?page=2", p.getResult().getNextLink()); + assertEquals(Arrays.asList("apple", "mango", "orange"), p.getResult().getData()); + assertEquals(p.getResult().getData(), p.getItems()); + assertEquals(SUCCESS_STATUS_CODE, p.getStatusCode()); + assertEquals(getHttpHeaders(), p.getHeaders()); + pages.add(p); + + assertTrue(pagesIterator.hasNext()); + p = pagesIterator.next().get(); + assertNotNull(p); + assertEquals("vegitables", p.getResult().getPageInfo()); + assertEquals("https://localhost:3000/path?page=3", p.getResult().getNextLink()); + assertEquals(Arrays.asList("potato", "carrot", "tomato"), p.getResult().getData()); + assertEquals(p.getResult().getData(), p.getItems()); + assertEquals(SUCCESS_STATUS_CODE, p.getStatusCode()); + assertEquals(getHttpHeaders(), p.getHeaders()); + pages.add(p); + + assertFalse(pagesIterator.hasNext()); exception = assertThrows(NoSuchElementException.class, pagesIterator::next); assertEquals("No more pages available.", exception.getMessage()); + + return pages; } private PaginatedData, diff --git a/src/test/java/apimatic/core/utilities/CoreHelperTest.java b/src/test/java/apimatic/core/utilities/CoreHelperTest.java index ede7b802..91ebcd06 100644 --- a/src/test/java/apimatic/core/utilities/CoreHelperTest.java +++ b/src/test/java/apimatic/core/utilities/CoreHelperTest.java @@ -1863,11 +1863,6 @@ public void testUpdateValueByPointerInvalidCases() { .updateValueByPointer(atoms, "/atom/NumberOfProtons", v -> null); assertEquals(atoms, result); - result = CoreHelper - .updateValueByPointer(atoms, "/nucleas/NumberOfProtons", - v -> NUMBER_OF_NEUTRONS); - assertEquals(atoms, result); - result = CoreHelper .updateValueByPointer(atoms, "/atom", v -> new Atom(UPDATED_VALUE, UPDATED_VALUE)); From f0a9e8b793fac6343e7887ad11d68ff86a85bcaa Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Fri, 13 Jun 2025 15:20:33 +0500 Subject: [PATCH 30/33] add doc block comment --- src/main/java/io/apimatic/core/types/pagination/PageWrapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 87befc41..1de2baf5 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -15,6 +15,7 @@ public final class PageWrapper implements ApiResponseType

{ * @param response Response from API call. * @param page Page to be wrapped. * @param items Extracted items from the page. + * @param strategy Pagination strategy to be applied. * @return An new instance of PageWrapper. */ public static PageWrapper create(Response response, P page, From ffc156e932a795febf0abe675e8564264eacff6b Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Fri, 13 Jun 2025 15:22:58 +0500 Subject: [PATCH 31/33] fix spell in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ae7e954..4b61cd04 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Core lib's Maven group ID is `io.apimatic`, and its artifact ID is `core`. | [`CoreApiException`](./src/main/java/io/apimatic/core/types/CoreApiException.java) | This is the base class for all exceptions that represent an error response from the server | | [`MultipartFileWrapper`](./src/main/java/io/apimatic/core/types/http/request/MultipartFileWrapper.java) | To wrap file and headers to be sent as part of a multipart request | | [`MultipartWrapper`](./src/main/java/io/apimatic/core/types/http/request/MultipartWrapper.java) | To wrap byteArray and headers to be sent as part of a multipart request | -| [`PaginatedData`](./src/main/java/io/apimatic/core/types/pagination/PaginatedData.java) | To provide pagination functionality for both syncrounous and asyncrounous pagination types | +| [`PaginatedData`](./src/main/java/io/apimatic/core/types/pagination/PaginatedData.java) | To provide pagination functionality for both synchronous and asynchronous pagination types | | [`PageWrapper`](./src/main/java/io/apimatic/core/types/pagination/PageWrapper.java) | To wrap a single page along with its items and meta-data in the paginated data | | [`CursorPagination`](./src/main/java/io/apimatic/core/types/pagination/CursorPagination.java) | Provides cursor based pagination strategy | | [`LinkPagination`](./src/main/java/io/apimatic/core/types/pagination/LinkPagination.java) | Provides link based pagination strategy | From c83f90d5255246fe2055f930eb38c40deda8318e Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Fri, 13 Jun 2025 15:25:05 +0500 Subject: [PATCH 32/33] fix remaining doc block issues --- .../io/apimatic/core/types/pagination/PageWrapper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java index 1de2baf5..9cef4613 100644 --- a/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java +++ b/src/main/java/io/apimatic/core/types/pagination/PageWrapper.java @@ -75,7 +75,7 @@ public boolean isNumberPagination() { /** * Sets the next link input - * @param strategy value of next link input + * @param nextLinkInput value of next link input */ public void setNextLinkInput(String nextLinkInput) { this.nextLinkInput = nextLinkInput; @@ -91,7 +91,7 @@ public String getNextLinkInput() { /** * Sets the cursor input - * @param strategy value of cursor input + * @param cursorInput value of cursor input */ public void setCursorInput(String cursorInput) { this.cursorInput = cursorInput; @@ -107,7 +107,7 @@ public String getCursorInput() { /** * Sets the offset input - * @param strategy value of offset input + * @param offsetInput value of offset input */ public void setOffsetInput(int offsetInput) { this.offsetInput = offsetInput; @@ -123,7 +123,7 @@ public int getOffsetInput() { /** * Sets the page input - * @param strategy value of page input + * @param pageInput value of page input */ public void setPageInput(int pageInput) { this.pageInput = pageInput; From ec95968ba441b2a0d22b8d3ef701c0ae09285926 Mon Sep 17 00:00:00 2001 From: Asad Ali <14asadali@gmail.com> Date: Fri, 13 Jun 2025 15:57:38 +0500 Subject: [PATCH 33/33] fix linting issues --- .../type/pagination/OffsetPaginationTest.java | 6 +++-- .../type/pagination/PaginatedDataTest.java | 27 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java index 95fdbb5b..56006a7a 100644 --- a/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java +++ b/src/test/java/apimatic/core/type/pagination/OffsetPaginationTest.java @@ -330,6 +330,7 @@ public void testValidStringOffset() { @Test public void testInvalidStringOffset() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()) .thenReturn(new HttpRequest.Builder().queryParam( @@ -344,7 +345,7 @@ public void testInvalidStringOffset() { assertEquals(INVALID_OFFSET_STRING, v); return v; }); - PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null, offset); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); assertTrue(pageWrapper.isOffsetPagination()); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } @@ -352,6 +353,7 @@ public void testInvalidStringOffset() { @Test public void testMissingOffset() { PaginatedData paginatedData = mock(PaginatedData.class); + Response response = mock(Response.class); when(paginatedData.getRequestBuilder()).thenReturn(new HttpRequest.Builder()); when(paginatedData.getPageSize()).thenReturn(PAGE_SIZE); @@ -360,7 +362,7 @@ public void testMissingOffset() { Builder requestBuilder = offset.apply(paginatedData); assertNotNull(requestBuilder); - PageWrapper pageWrapper = PageWrapper.create(mock(Response.class), null, null, offset); + PageWrapper pageWrapper = PageWrapper.create(response, null, null, offset); assertTrue(pageWrapper.isOffsetPagination()); assertEquals(INVALID_OFFSET_RESULT, pageWrapper.getOffsetInput()); } diff --git a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java index 069dc37b..3e06d365 100644 --- a/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java +++ b/src/test/java/apimatic/core/type/pagination/PaginatedDataTest.java @@ -101,7 +101,7 @@ public void testInvalidPaginationWithNullItems() throws IOException { public void testInvalidPaginationWithNullPage() throws IOException { assertInvalidPaginatedData(null); } - + private void assertInvalidPaginatedData(String responseBody) throws IOException { Runnable call1 = () -> when(getResponse().getBody()).thenReturn(responseBody); PaginatedData, @@ -161,8 +161,8 @@ public void testCursorPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - List> pages = verifyData(getPaginatedData(call1, call2, call3, - new CursorPagination("$response.body#/page_info", + List> pages = verifyData(getPaginatedData(call1, call2, + call3, new CursorPagination("$response.body#/page_info", "$request.path#/cursor"))); assertTrue(pages.get(0).isCursorPagination()); @@ -208,13 +208,13 @@ public void testOffsetPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - List> pages = verifyData(getPaginatedData(call1, call2, call3, - new OffsetPagination("$request.headers#/offset"))); + List> pages = verifyData(getPaginatedData(call1, call2, + call3, new OffsetPagination("$request.headers#/offset"))); assertTrue(pages.get(0).isOffsetPagination()); assertEquals(0, pages.get(0).getOffsetInput()); assertTrue(pages.get(1).isOffsetPagination()); - assertEquals(3, pages.get(1).getOffsetInput()); + assertEquals(PAGE_SIZE, pages.get(1).getOffsetInput()); } @Test @@ -246,8 +246,8 @@ public void testPagePaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - List> pages = verifyData(getPaginatedData(call1, call2, call3, - new PagePagination("$request.query#/page"))); + List> pages = verifyData(getPaginatedData(call1, call2, + call3, new PagePagination("$request.query#/page"))); assertTrue(pages.get(0).isNumberPagination()); assertEquals(1, pages.get(0).getPageInput()); @@ -284,8 +284,8 @@ public void testMultiPaginationData() throws IOException, CoreApiException { Runnable call3 = () -> when(getResponse().getBody()).thenReturn( "{\"data\":[]}"); - List> pages = verifyData(getPaginatedData(call1, call2, call3, - new LinkPagination("$response.INVALID#/next_link"), + List> pages = verifyData(getPaginatedData(call1, call2, + call3, new LinkPagination("$response.INVALID#/next_link"), new PagePagination("$request.body#/limit"))); assertTrue(pages.get(0).isLinkPagination()); @@ -384,7 +384,8 @@ public void testMultiPaginationDataAsync() assertEquals(0, paginatedData.getItems(itemCreator).size()); } - private List> verifyData(PaginatedData, + private List> verifyData( + PaginatedData, RecordPage, CoreApiException> paginatedData) throws CoreApiException, IOException { Iterator> itemIterator = paginatedData.items(cs -> cs); @@ -407,7 +408,7 @@ private List> verifyData(PaginatedData> pages = new ArrayList<>(); - + Iterator, CoreApiException>> pagesIterator = paginatedData.pages(cs -> cs); @@ -437,7 +438,7 @@ private List> verifyData(PaginatedData