Commit 68e98e6b authored by Greg Messner's avatar Greg Messner
Browse files

Added support for api_kaminari_count_with_limit (#305).

parent 5dba51af
...@@ -45,6 +45,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -45,6 +45,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
private int totalPages; private int totalPages;
private int totalItems; private int totalItems;
private int currentPage; private int currentPage;
private int kaminariNextPage;
private List<String> pageParam = new ArrayList<>(1); private List<String> pageParam = new ArrayList<>(1);
private List<T> currentItems; private List<T> currentItems;
...@@ -92,41 +93,74 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -92,41 +93,74 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
throw new GitLabApiException(e); throw new GitLabApiException(e);
} }
if (currentItems == null) {
throw new GitLabApiException("Invalid response from from GitLab server");
}
this.api = api; this.api = api;
this.queryParams = queryParams; this.queryParams = queryParams;
this.pathArgs = pathArgs; 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;
}
try { totalPages = getIntHeaderValue(response, TOTAL_PAGES_HEADER);
this.itemsPerPage = getHeaderValue(response, PER_PAGE); totalItems = getIntHeaderValue(response, TOTAL_HEADER);
totalPages = getHeaderValue(response, TOTAL_PAGES_HEADER);
totalItems = getHeaderValue(response, TOTAL_HEADER); // Since GitLab 11.8 and behind the api_kaminari_count_with_limit feature flag,
} catch (GitLabApiException glae) { // 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.
// Some API endpoints do not return the proper headers, check for that condition and act accordingly if (totalPages == -1 || totalItems == -1) {
if (currentItems != null && currentItems.size() < itemsPerPage) {
this.itemsPerPage = itemsPerPage; int nextPage = getIntHeaderValue(response, NEXT_PAGE_HEADER);
if (nextPage < 2) {
totalPages = 1; totalPages = 1;
totalItems = currentItems.size(); totalItems = currentItems.size();
} else { } else {
throw (glae); kaminariNextPage = 2;
} }
} }
} }
/** /**
* Get the specified integer header value from the Response instance. * Get the specified header value from the Response instance.
* *
* @param response the Response instance to get the value from * @param response the Response instance to get the value from
* @param key the HTTP header key to get the value for * @param key the HTTP header key to get the value for
* @return the specified integer header value from the Response instance * @return the specified header value from the Response instance, or null if the header is not present
* @throws GitLabApiException if any error occurs * @throws GitLabApiException if any error occurs
*/ */
private int getHeaderValue(Response response, String key) throws GitLabApiException { private String getHeaderValue(Response response, String key) throws GitLabApiException {
String value = response.getHeaderString(key); String value = response.getHeaderString(key);
value = (value != null ? value.trim() : null); value = (value != null ? value.trim() : null);
if (value == null || value.length() == 0) if (value == null || value.length() == 0) {
throw new GitLabApiException("Missing '" + key + "' header from server"); 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 { try {
return (Integer.parseInt(value)); return (Integer.parseInt(value));
...@@ -157,7 +191,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -157,7 +191,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
/** /**
* Get the total number of pages returned by the GitLab API. * Get the total number of pages returned by the GitLab API.
* *
* @return 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() { public int getTotalPages() {
return (totalPages); return (totalPages);
...@@ -166,7 +200,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -166,7 +200,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
/** /**
* Get the total number of items (T instances) returned by the GitLab API. * 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 * @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() { public int getTotalItems() {
return (totalItems); return (totalItems);
...@@ -188,7 +222,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -188,7 +222,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
*/ */
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return (currentPage < totalPages); return (currentPage < totalPages || currentPage < kaminariNextPage);
} }
/** /**
...@@ -230,6 +264,11 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -230,6 +264,11 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
* @throws GitLabApiException if any error occurs * @throws GitLabApiException if any error occurs
*/ */
public List<T> last() throws GitLabApiException { public List<T> last() throws GitLabApiException {
if (kaminariNextPage != 0) {
throw new GitLabApiException("Kaminari count limit exceeded, unable to fetch last page");
}
return (page(totalPages)); return (page(totalPages));
} }
...@@ -263,7 +302,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -263,7 +302,7 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
*/ */
public List<T> page(int pageNumber) { public List<T> page(int pageNumber) {
if (pageNumber > totalPages) { if (pageNumber > totalPages && pageNumber > kaminariNextPage) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} else if (pageNumber < 1) { } else if (pageNumber < 1) {
throw new NoSuchElementException(); throw new NoSuchElementException();
...@@ -284,6 +323,11 @@ public class Pager<T> implements Iterator<List<T>>, Constants { ...@@ -284,6 +323,11 @@ public class Pager<T> implements Iterator<List<T>>, Constants {
Response response = api.get(Response.Status.OK, queryParams, pathArgs); Response response = api.get(Response.Status.OK, queryParams, pathArgs);
currentItems = mapper.readValue((InputStream) response.getEntity(), javaType); currentItems = mapper.readValue((InputStream) response.getEntity(), javaType);
currentPage = pageNumber; currentPage = pageNumber;
if (kaminariNextPage > 0) {
kaminariNextPage = getIntHeaderValue(response, NEXT_PAGE_HEADER);
}
return (currentItems); return (currentItems);
} catch (GitLabApiException | IOException e) { } catch (GitLabApiException | IOException e) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment