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

org.aoju.bus.gitlab.Pager Maven / Gradle / Ivy

/*********************************************************************************
 *                                                                               *
 * The MIT License (MIT)                                                         *
 *                                                                               *
 * Copyright (c) 2015-2022 aoju.org Greg Messner and other contributors.         *
 *                                                                               *
 * Permission is hereby granted, free of charge, to any person obtaining a copy  *
 * of this software and associated documentation files (the "Software"), to deal *
 * in the Software without restriction, including without limitation the rights  *
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell     *
 * copies of the Software, and to permit persons to whom the Software is         *
 * furnished to do so, subject to the following conditions:                      *
 *                                                                               *
 * The above copyright notice and this permission notice shall be included in    *
 * all copies or substantial portions of the Software.                           *
 *                                                                               *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR    *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,      *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER        *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN     *
 * THE SOFTWARE.                                                                 *
 *                                                                               *
 ********************************************************************************/
package org.aoju.bus.gitlab;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aoju.bus.gitlab.support.JacksonJson;

import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * 

This class defines an Iterator implementation that is used as a paging iterator for all API methods that * return a List of objects. It hides the details of interacting with the GitLab API when paging is involved * simplifying accessing large lists of objects.

* *

Example usage:

* *
 *   // Get a Pager instance that will page through the projects with 10 projects per page
 *   Pager<Project> projectPager = gitlabApi.getProjectsApi().getProjectsPager(10);
 *
 *   // Iterate through the pages and print out the name and description
 *   while (projectsPager.hasNext())) {
 *       List<Project> projects = projectsPager.next();
 *       for (Project project : projects) {
 *           System.out.println(project.getName() + " : " + project.getDescription());
 *       }
 *   }
 * 
* * @param the GitLab4J type contained in the List. */ public class Pager implements Iterator>, Constants { private static JacksonJson jacksonJson = new JacksonJson(); private static ObjectMapper mapper = jacksonJson.getObjectMapper(); private int itemsPerPage; private int totalPages; private int totalItems; private int currentPage; private int kaminariNextPage; private List pageParam = new ArrayList<>(1); private List currentItems; private Stream pagerStream = null; private AbstractApi api; private MultivaluedMap queryParams; private Object[] pathArgs; private JavaType javaType; /** * Creates a Pager instance to access the API through the specified path and query parameters. * * @param api the AbstractApi implementation to communicate through * @param type the GitLab4J type that will be contained in the List * @param itemsPerPage items per page * @param queryParams HTTP query params * @param pathArgs HTTP path arguments * @throws GitLabApiException if any error occurs */ Pager(AbstractApi api, Class type, int itemsPerPage, MultivaluedMap queryParams, Object... pathArgs) throws GitLabApiException { javaType = mapper.getTypeFactory().constructCollectionType(List.class, type); if (itemsPerPage < 1) { itemsPerPage = api.getDefaultPerPage(); } // Make sure the per_page parameter is present if (queryParams == null) { queryParams = new GitLabApiForm().withParam(PER_PAGE_PARAM, itemsPerPage).asMap(); } else { queryParams.remove(PER_PAGE_PARAM); queryParams.add(PER_PAGE_PARAM, Integer.toString(itemsPerPage)); } // Set the page param to 1 pageParam = new ArrayList<>(); pageParam.add("1"); queryParams.put(PAGE_PARAM, pageParam); Response response = api.get(Response.Status.OK, queryParams, pathArgs); try { currentItems = mapper.readValue((InputStream) response.getEntity(), javaType); } catch (Exception e) { throw new GitLabApiException(e); } if (currentItems == null) { throw new GitLabApiException("Invalid response from from GitLab server"); } this.api = api; this.queryParams = queryParams; this.pathArgs = pathArgs; this.itemsPerPage = getIntHeaderValue(response, PER_PAGE); // Some API endpoints do not return the "X-Per-Page" header when there is only 1 page, check for that condition and act accordingly if (this.itemsPerPage == -1) { this.itemsPerPage = itemsPerPage; totalPages = 1; totalItems = currentItems.size(); return; } totalPages = getIntHeaderValue(response, TOTAL_PAGES_HEADER); totalItems = getIntHeaderValue(response, TOTAL_HEADER); // Since GitLab 11.8 and behind the api_kaminari_count_with_limit feature flag, // if the number of resources is more than 10,000, the X-Total and X-Total-Page // headers as well as the rel="last" Link are not present in the response headers. if (totalPages == -1 || totalItems == -1) { int nextPage = getIntHeaderValue(response, NEXT_PAGE_HEADER); if (nextPage < 2) { totalPages = 1; totalItems = currentItems.size(); } else { kaminariNextPage = 2; } } } /** * Get the specified header value from the Response instance. * * @param response the Response instance to get the value from * @param key the HTTP header key to get the value for * @return the specified header value from the Response instance, or null if the header is not present * @throws GitLabApiException if any error occurs */ private String getHeaderValue(Response response, String key) throws GitLabApiException { String value = response.getHeaderString(key); value = (value != null ? value.trim() : null); if (value == null || value.length() == 0) { return (null); } return (value); } /** * Get the specified integer header value from the Response instance. * * @param response the Response instance to get the value from * @param key the HTTP header key to get the value for * @return the specified integer header value from the Response instance, or -1 if the header is not present * @throws GitLabApiException if any error occurs */ private int getIntHeaderValue(Response response, String key) throws GitLabApiException { String value = getHeaderValue(response, key); if (value == null) { return -1; } try { return (Integer.parseInt(value)); } catch (NumberFormatException nfe) { throw new GitLabApiException("Invalid '" + key + "' header value (" + value + ") from server"); } } /** * Sets the "page" query parameter. * * @param page the value for the "page" query parameter */ private void setPageParam(int page) { pageParam.set(0, Integer.toString(page)); queryParams.put(PAGE_PARAM, pageParam); } /** * Get the items per page value. * * @return the items per page value */ public int getItemsPerPage() { return (itemsPerPage); } /** * Get the total number of pages returned by the GitLab API. * * @return the total number of pages returned by the GitLab API, or -1 if the Kaminari limit of 10,000 has been exceeded */ public int getTotalPages() { return (totalPages); } /** * Get the total number of items (T instances) returned by the GitLab API. * * @return the total number of items (T instances) returned by the GitLab API, or -1 if the Kaminari limit of 10,000 has been exceeded */ public int getTotalItems() { return (totalItems); } /** * Get the current page of the iteration. * * @return the current page of the iteration */ public int getCurrentPage() { return (currentPage); } /** * Returns the true if there are additional pages to iterate over, otherwise returns false. * * @return true if there are additional pages to iterate over, otherwise returns false */ @Override public boolean hasNext() { return (currentPage < totalPages || currentPage < kaminariNextPage); } /** * Returns the next List in the iteration containing the next page of objects. * * @return the next List in the iteration * @throws NoSuchElementException if the iteration has no more elements * @throws RuntimeException if a GitLab API error occurs, will contain a wrapped GitLabApiException with the details of the error */ @Override public List next() { return (page(currentPage + 1)); } /** * This method is not implemented and will throw an UnsupportedOperationException if called. * * @throws UnsupportedOperationException when invoked */ @Override public void remove() { throw new UnsupportedOperationException(); } /** * Returns the first page of List. Will rewind the iterator. * * @return the first page of List * @throws GitLabApiException if any error occurs */ public List first() throws GitLabApiException { return (page(1)); } /** * Returns the last page of List. Will set the iterator to the end. * * @return the last page of List * @throws GitLabApiException if any error occurs */ public List last() throws GitLabApiException { if (kaminariNextPage != 0) { throw new GitLabApiException("Kaminari count limit exceeded, unable to fetch last page"); } return (page(totalPages)); } /** * Returns the previous page of List. Will set the iterator to the previous page. * * @return the previous page of List * @throws GitLabApiException if any error occurs */ public List previous() throws GitLabApiException { return (page(currentPage - 1)); } /** * Returns the current page of List. * * @return the current page of List * @throws GitLabApiException if any error occurs */ public List current() throws GitLabApiException { return (page(currentPage)); } /** * Returns the specified page of List. * * @param pageNumber the page to get * @return the specified page of List * @throws NoSuchElementException if the iteration has no more elements * @throws RuntimeException if a GitLab API error occurs, will contain a wrapped GitLabApiException with the details of the error */ public List page(int pageNumber) { if (currentPage == 0 && pageNumber == 1) { currentPage = 1; return (currentItems); } if (currentPage == pageNumber) { return (currentItems); } if (pageNumber > totalPages && pageNumber > kaminariNextPage) { throw new NoSuchElementException(); } else if (pageNumber < 1) { throw new NoSuchElementException(); } try { setPageParam(pageNumber); Response response = api.get(Response.Status.OK, queryParams, pathArgs); currentItems = mapper.readValue((InputStream) response.getEntity(), javaType); currentPage = pageNumber; if (kaminariNextPage > 0) { kaminariNextPage = getIntHeaderValue(response, NEXT_PAGE_HEADER); } return (currentItems); } catch (GitLabApiException | IOException e) { throw new RuntimeException(e); } } /** * Gets all the items from each page as a single List instance. * * @return all the items from each page as a single List instance * @throws GitLabApiException if any error occurs */ public List all() throws GitLabApiException { // Make sure that current page is 0, this will ensure the whole list is fetched // regardless of what page the instance is currently on. currentPage = 0; List allItems = new ArrayList<>(Math.max(totalItems, 0)); // Iterate through the pages and append each page of items to the list while (hasNext()) { allItems.addAll(next()); } return (allItems); } /** * Builds and returns a Stream instance which is pre-populated with all items from all pages. * * @return a Stream instance which is pre-populated with all items from all pages * @throws IllegalStateException if Stream has already been issued * @throws GitLabApiException if any other error occurs */ public Stream stream() throws GitLabApiException, IllegalStateException { if (pagerStream == null) { synchronized (this) { if (pagerStream == null) { // Make sure that current page is 0, this will ensure the whole list is streamed // regardless of what page the instance is currently on. currentPage = 0; // Create a Stream.Builder to contain all the items. This is more efficient than // getting a List with all() and streaming that List Stream.Builder streamBuilder = Stream.builder(); // Iterate through the pages and append each page of items to the stream builder while (hasNext()) { next().forEach(streamBuilder); } pagerStream = streamBuilder.build(); return (pagerStream); } } } throw new IllegalStateException("Stream already issued"); } /** * Creates a Stream instance for lazily streaming items from the GitLab server. * * @return a Stream instance for lazily streaming items from the GitLab server * @throws IllegalStateException if Stream has already been issued */ public Stream lazyStream() throws IllegalStateException { if (pagerStream == null) { synchronized (this) { if (pagerStream == null) { // Make sure that current page is 0, this will ensure the whole list is streamed // regardless of what page the instance is currently on. currentPage = 0; pagerStream = StreamSupport.stream(new PagerSpliterator(this), false); return (pagerStream); } } } throw new IllegalStateException("Stream already issued"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy