Skip to content

Commit

Permalink
Improve usability of Pagination and update code snippet (#1506)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvindkrishnakumar-okta authored Jun 14, 2024
1 parent f42e1b9 commit e3044f0
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 19 deletions.
16 changes: 6 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,21 +522,17 @@ BookmarkApplication createdApp = apiClient.invokeAPI(

### Pagination

Pagination info would be available via `PagedList` when the API response is a collection of models.
Collections can be fetched with manually controlled Pagination.

[//]: # (method: paginate)
```java
UserApi userApi = new UserApi(client);

// max number of items per page
int pageSize = 10;
PagedList<User> pagedUserList = new PagedList<>();
List<User> users = new ArrayList<>();
String after = null;
do {
pagedUserList = (PagedList<User>)
userApi.listUsers(null, pagedUserList.getAfter(), pageSize, null, null, null, null);

pagedUserList.forEach(usr -> log.info("User: {}", usr.getProfile().getEmail()));
} while (pagedUserList.hasMoreItems());
users.addAll(userApi.listUsers(null, after, 200, null, null, null, null));
after = PaginationUtil.getAfter(userApi.getApiClient());
} while (StringUtils.isNotBlank(after));
```
[//]: # (end: paginate)

Expand Down
113 changes: 113 additions & 0 deletions api/src/main/java/com/okta/sdk/helper/PaginationUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.okta.sdk.helper;

import com.okta.commons.lang.Assert;
import com.okta.sdk.resource.client.ApiClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Helper class for Pagination related functions.
*
* @since 16.0.0
*/
public class PaginationUtil {

private final static Logger log = LoggerFactory.getLogger(PaginationUtil.class);

/**
* Gets the 'after' resource id from ApiClient instance.
* e.g. a next page URL such as <a href="https://example.okta.com/api/v1/users?after=100uekbsy586JToqdQ1d7&limit=100">...</a> will fetch
* an output 'after' string value of 100uekbsy586JToqdQ1d7
*
* @param apiClient {@link ApiClient} instance
* @return the 'after' resource id
*/
public static String getAfter(ApiClient apiClient) {
return getAfter(getNextPage(apiClient));
}

/**
* Gets the 'after' resource id from the next page URL string.
* e.g. a next page URL such as <a href="https://example.okta.com/api/v1/users?after=100uekbsy586JToqdQ1d7&limit=100">...</a> will fetch
* an output 'after' string value of 100uekbsy586JToqdQ1d7
*
* @param nextPage the next page URL string
* @return the 'after' resource id
*/
private static String getAfter(String nextPage) {

String after;
URL url;

try {
url = new URL(nextPage);
after = splitQuery(url).get("after");
log.debug("after: {}", after);
return after;
} catch (MalformedURLException | UnsupportedEncodingException e) {
return null;
}
}

/**
* Gets the Next Page URL (to paginate) from ApiClient response header.
*
* @param apiClient the {@link ApiClient} instance
* @return the next page URL string
*/
private static String getNextPage(ApiClient apiClient) {

Assert.notNull(apiClient, "apiClient cannot be null");
Assert.notNull(apiClient.getResponseHeaders(), "apiClient is missing response headers");
Assert.notNull(apiClient.getResponseHeaders().get("link"), "apiClient is missing 'link' response headers");

List<String> linkHeaders = apiClient.getResponseHeaders().get("link");

String nextPage = null;

for (String linkHeader : linkHeaders) {
String[] parts = linkHeader.split("; *");
String url = parts[0]
.replaceAll("<", "")
.replaceAll(">", "");
String rel = parts[1];
if (rel.equals("rel=\"next\"")) {
nextPage = url;
}
}

log.debug("Next Page: {}", nextPage);
return nextPage;
}

/**
* Split a URL with query strings into name value pairs.
*
* @param url the url to split
* @return map of query string name value pairs
* @throws UnsupportedEncodingException If character encoding needs to be consulted
*/
private static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {

Assert.notNull(url, "url cannot be null");

Map<String, String> query_pairs = new LinkedHashMap<>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int index = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, index), StandardCharsets.UTF_8.name()),
URLDecoder.decode(pair.substring(index + 1), StandardCharsets.UTF_8.name()));
}
return query_pairs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;

@Deprecated
public class PagedList<T> extends ArrayList<T> {

private String self;
Expand Down
16 changes: 7 additions & 9 deletions examples/quickstart/src/main/java/quickstart/ReadmeSnippets.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.okta.sdk.client.AuthenticationScheme;
import com.okta.sdk.client.AuthorizationMode;
import com.okta.sdk.client.Clients;
import com.okta.sdk.resource.common.PagedList;
import com.okta.sdk.helper.PaginationUtil;
import com.okta.sdk.resource.group.GroupBuilder;
import com.okta.sdk.resource.user.UserBuilder;

Expand All @@ -32,6 +32,7 @@
import com.okta.sdk.resource.api.*;
import com.okta.sdk.resource.model.*;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -337,16 +338,13 @@ private void callAnotherEndpoint() throws ApiException {
private void paginate() throws ApiException {
UserApi userApi = new UserApi(client);

int pageSize = 10; // max number of items per page

PagedList<User> pagedUserList = new PagedList<>();
List<User> users = new ArrayList<>();
String after = null;

do {
pagedUserList = (PagedList<User>)
userApi.listUsers(null, pagedUserList.getAfter(), pageSize, null, null, null, null);

pagedUserList.forEach(usr -> log.info("User: {}", usr.getProfile().getEmail()));
} while (pagedUserList.hasMoreItems());
users.addAll(userApi.listUsers(null, after, 200, null, null, null, null));
after = PaginationUtil.getAfter(userApi.getApiClient());
} while (StringUtils.isNotBlank(after));
}

private void complexCaching() {
Expand Down

0 comments on commit e3044f0

Please sign in to comment.