diff --git a/README.md b/README.md index c71d4017ee4..fd459d8838b 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ For more examples of handling collections see the [paging](#paging) section belo UserApi userApi = new UserApi(client); // search by email -List users = userApi.listUsers(null, null, 5, null, "jcoder@example.com", null, null); +List users = userApi.listUsers(null, null, 5, null, "profile.email eq \"jcoder@example.com\"", null, null); // filter parameter users = userApi.listUsers(null, null, null, "status eq \"ACTIVE\"",null, null, null); @@ -437,29 +437,19 @@ Every instance of the SDK `Client` is thread-safe. You **should** use the same i ## Paging -Paging is handled automatically when iterating over a collection. - [//]: # (method: paging) ```java UserApi userApi = new UserApi(client); +int limit = 2; +PagedList pagedUserList = userApi.listUsersWithPaginationInfo(null, null, limit, null, null, null, null); -// limit -int pageSize = 2; -PagedList usersPagedListOne = userApi.listUsersWithPaginationInfo(null, null, pageSize, null, null, null, null); - -// e.g. https://example.okta.com/api/v1/users?after=000u3pfv9v4SQXvpBB0g7&limit=2 -String nextPageUrl = usersPagedListOne.getNextPage(); - -// replace 'after' with actual cursor from the nextPageUrl -PagedList usersPagedListTwo = userApi.listUsersWithPaginationInfo("after", null, pageSize, null, null, null, null); - -// loop through all of them (paging is automatic) -for (User tmpUser : usersPagedListOne.getItems()) { +// loop through all of them +for (User tmpUser : pagedUserList.getItems()) { log.info("User: {}", tmpUser.getProfile().getEmail()); } // or stream -usersPagedListOne.getItems().forEach(tmpUser -> log.info("User: {}", tmpUser.getProfile().getEmail())); +pagedUserList.getItems().forEach(tmpUser -> log.info("User: {}", tmpUser.getProfile().getEmail())); ``` [//]: # (end: paging) diff --git a/api/src/main/java/com/okta/sdk/error/ErrorHandler.java b/api/src/main/java/com/okta/sdk/error/ErrorHandler.java index 3b032e84dd3..44e5a5d3540 100644 --- a/api/src/main/java/com/okta/sdk/error/ErrorHandler.java +++ b/api/src/main/java/com/okta/sdk/error/ErrorHandler.java @@ -51,7 +51,7 @@ public void handleError(ClientHttpResponse httpResponse) throws IOException, Res final String message = new String(FileCopyUtils.copyToByteArray(httpResponse.getBody())); if (!isValid(message)) { - throw new ResourceException(new NonJsonError(message)); + throw new ResourceException(new NonJsonError(statusCode, message)); } final Map errorMap = mapper.readValue(message, Map.class); diff --git a/api/src/main/java/com/okta/sdk/error/NonJsonError.java b/api/src/main/java/com/okta/sdk/error/NonJsonError.java index 875b32b0544..54a1f15bf25 100644 --- a/api/src/main/java/com/okta/sdk/error/NonJsonError.java +++ b/api/src/main/java/com/okta/sdk/error/NonJsonError.java @@ -20,15 +20,17 @@ public class NonJsonError implements Error { + private final int status; private final String message; - public NonJsonError(String message) { + public NonJsonError(int status, String message) { + this.status = status; this.message = message; } @Override public int getStatus() { - return 0; + return status; } @Override diff --git a/api/src/main/java/com/okta/sdk/resource/common/PagedList.java b/api/src/main/java/com/okta/sdk/resource/common/PagedList.java index 671f05caa78..6a91080d3a1 100644 --- a/api/src/main/java/com/okta/sdk/resource/common/PagedList.java +++ b/api/src/main/java/com/okta/sdk/resource/common/PagedList.java @@ -15,7 +15,17 @@ */ package com.okta.sdk.resource.common; +import com.okta.commons.lang.Assert; +import org.springframework.http.ResponseEntity; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,6 +47,16 @@ public String getNextPage() { return nextPage; } + public String getAfter(String nextPageUrl) { + URL url; + try { + url = new URL(nextPageUrl); + return splitQuery(url).get("after"); + } catch (MalformedURLException | UnsupportedEncodingException e) { + return null; + } + } + public void setSelf(String self) { this.self = self; } @@ -58,4 +78,42 @@ private List flatten(List list) { .flatMap(e -> e instanceof List ? flatten((List) e).stream() : Stream.of(e)) .collect(Collectors.toList()); } + + public static PagedList constructPagedList(ResponseEntity responseEntity) { + + PagedList pagedList = new PagedList(); + Assert.notNull(responseEntity); + pagedList.addItems(Collections.singletonList(responseEntity.getBody())); + List linkHeaders = responseEntity.getHeaders().get("link"); + Assert.notNull(linkHeaders); + for (String link : linkHeaders) { + String[] parts = link.split("; *"); + String url = parts[0] + .replaceAll("<", "") + .replaceAll(">", ""); + String rel = parts[1]; + if (rel.equals("rel=\"next\"")) { + pagedList.setNextPage(url); + } else if (rel.equals("rel=\"self\"")) { + pagedList.setSelf(url); + } + } + return pagedList; + } + + /** + * Split a URL with query strings into name value pairs. + * @param url the url to split + * @return map of query string name value pairs + */ + private static Map splitQuery(URL url) throws UnsupportedEncodingException { + Map 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), "UTF-8"), URLDecoder.decode(pair.substring(index + 1), "UTF-8")); + } + return query_pairs; + } } diff --git a/api/src/main/resources/custom_templates/ApiClient.mustache b/api/src/main/resources/custom_templates/ApiClient.mustache index ef0d3986a3a..7455d678100 100644 --- a/api/src/main/resources/custom_templates/ApiClient.mustache +++ b/api/src/main/resources/custom_templates/ApiClient.mustache @@ -40,10 +40,17 @@ import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.http.RequestEntity.BodyBuilder; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.client.config.RequestConfig; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.StreamUtils; +import java.nio.charset.Charset; {{#withXml}} import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; @@ -896,7 +903,7 @@ List currentInterceptors = this.restTemplate.getIn RestTemplate restTemplate = new RestTemplate(messageConverters); {{/withXml}}{{^withXml}}RestTemplate restTemplate = new RestTemplate();{{/withXml}} // This allows us to read the response more than once - Necessary for debugging. - restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory())); + restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(getClientHttpRequestFactory())); // disable default URL encoding DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); @@ -905,29 +912,45 @@ List currentInterceptors = this.restTemplate.getIn return restTemplate; } + private ClientHttpRequestFactory getClientHttpRequestFactory() { + int timeout = 5000; //TODO: set from config + + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout) + .build(); + + CloseableHttpClient client = HttpClientBuilder + .create() + .setDefaultRequestConfig(config) + .build(); + + return new HttpComponentsClientHttpRequestFactory(client); + } + protected RetryTemplate buildRetryTemplate() { - Map, Boolean> retryableExceptions = new HashMap<>(); - retryableExceptions.put(RetryableException.class, Boolean.TRUE); + Map, Boolean> retryableExceptions = new HashMap<>(); + retryableExceptions.put(RetryableException.class, Boolean.TRUE); - SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5, retryableExceptions); //TODO - get this from client config - FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5, retryableExceptions); //TODO - get this from client config + FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); - RetryTemplate retryTemplate = new RetryTemplate(); - retryTemplate.setRetryPolicy(retryPolicy); - retryTemplate.setBackOffPolicy(backOffPolicy); - retryTemplate.setThrowLastExceptionOnExhausted(true); - return retryTemplate; + RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(backOffPolicy); + retryTemplate.setThrowLastExceptionOnExhausted(true); + return retryTemplate; } - private ObjectMapper objectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - objectMapper.registerModule(new JavaTimeModule()); - objectMapper.registerModule(new JsonNullableModule()); - - return objectMapper; - } + private ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.registerModule(new JsonNullableModule()); + return objectMapper; + } /** * Update query and header parameters based on authentication settings. @@ -947,32 +970,30 @@ List currentInterceptors = this.restTemplate.getIn } private class ApiClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { - private final Log log = LogFactory.getLog(ApiClientHttpRequestInterceptor.class); + private final Log logger = LogFactory.getLog(ApiClientHttpRequestInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { - logRequest(request, body); - ClientHttpResponse response = execution.execute(request, body); - logResponse(response); - return response; + logRequest(request, body); + ClientHttpResponse response = execution.execute(request, body); + logResponse(response); + return response; } private void logRequest(HttpRequest request, byte[] body) throws UnsupportedEncodingException { - log.info("URI: " + request.getURI()); - log.info("HTTP Method: " + request.getMethod()); - log.info("HTTP Headers: " + headersToString(request.getHeaders())); - log.info("Request Body: " + new String(body, StandardCharsets.UTF_8)); + logger.info("URI: " + request.getMethod() + " " + request.getURI()); + logger.info("Request Headers: " + headersToString(request.getHeaders())); + logger.info("Request Body: " + new String(body, StandardCharsets.UTF_8)); } private void logResponse(ClientHttpResponse response) throws IOException { - log.info("HTTP Status Code: " + response.getRawStatusCode()); - log.info("Status Text: " + response.getStatusText()); - log.info("HTTP Headers: " + headersToString(response.getHeaders())); - log.info("Response Body: " + bodyToString(response.getBody())); + logger.info("Response Status: " + response.getRawStatusCode() + " " + response.getStatusText()); + logger.info("Response Headers: " + headersToString(response.getHeaders())); + logger.info("Response Body: " + StreamUtils.copyToString(response.getBody(), Charset.defaultCharset())); } private String headersToString(HttpHeaders headers) { - if(headers == null || headers.isEmpty()) { + if (headers == null || headers.isEmpty()) { return ""; } StringBuilder builder = new StringBuilder(); @@ -991,17 +1012,5 @@ List currentInterceptors = this.restTemplate.getIn builder.setLength(builder.length() - 1); // Get rid of trailing comma return builder.toString(); } - - private String bodyToString(InputStream body) throws IOException { - StringBuilder builder = new StringBuilder(); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(body, StandardCharsets.UTF_8)); - String line = bufferedReader.readLine(); - while (line != null) { - builder.append(line).append(System.lineSeparator()); - line = bufferedReader.readLine(); - } - bufferedReader.close(); - return builder.toString(); - } } } \ No newline at end of file diff --git a/api/src/main/resources/custom_templates/api.mustache b/api/src/main/resources/custom_templates/api.mustache index 825fbee8023..41726f08a6c 100644 --- a/api/src/main/resources/custom_templates/api.mustache +++ b/api/src/main/resources/custom_templates/api.mustache @@ -371,28 +371,21 @@ import org.openapitools.jackson.nullable.JsonNullableModule; {{#returnType}}ParameterizedTypeReference<{{{returnType}}}> localReturnType = new ParameterizedTypeReference<{{{returnType}}}>() {};{{/returnType}}{{^returnType}}ParameterizedTypeReference localReturnType = new ParameterizedTypeReference() {};{{/returnType}} ResponseEntity<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> responseEntity = apiClient.invokeAPI("{{{path}}}", HttpMethod.{{httpMethod}}, {{#hasPathParams}}uriVariables{{/hasPathParams}}{{^hasPathParams}}Collections.emptyMap(){{/hasPathParams}}, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType); {{#returnType}} - HttpHeaders headers = responseEntity.getHeaders(); - PagedList pagedList = new PagedList(); - List linkHeaders = headers.get("link"); - String self = null, nextPage = null; - for (String link : linkHeaders) { - if (link.contains("next")) { - nextPage = link.split(";")[0] - .replaceAll("<", "") - .replaceAll(">", ""); - } - if (link.contains("self")) { - self = link.split(";")[0] - .replaceAll("<", "") - .replaceAll(">", ""); - } - } - pagedList.addItems(Arrays.asList(responseEntity.getBody())); - if (Objects.nonNull(nextPage)) { - pagedList.setNextPage(nextPage); - } - if (Objects.nonNull(self)) { - pagedList.setSelf(self); + PagedList pagedList = PagedList.constructPagedList(responseEntity); + + String afterMarker = pagedList.getAfter(pagedList.getNextPage()); + + if (afterMarker != null) { + do { + localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "after", afterMarker)); + + ResponseEntity<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> newResponseEntity = apiClient.invokeAPI("{{{path}}}", HttpMethod.{{httpMethod}}, {{#hasPathParams}}uriVariables{{/hasPathParams}}{{^hasPathParams}}Collections.emptyMap(){{/hasPathParams}}, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType); + + PagedList newPagedList = PagedList.constructPagedList(newResponseEntity); + pagedList.getItems().addAll(newPagedList.getItems()); + pagedList.setNextPage(newPagedList.getNextPage()); + afterMarker = newPagedList.getAfter(newPagedList.getNextPage()); + } while (afterMarker != null); } return pagedList; {{/returnType}} diff --git a/api/src/main/resources/custom_templates/auth/ApiKeyAuth.mustache b/api/src/main/resources/custom_templates/auth/ApiKeyAuth.mustache new file mode 100644 index 00000000000..20f06525404 --- /dev/null +++ b/api/src/main/resources/custom_templates/auth/ApiKeyAuth.mustache @@ -0,0 +1,79 @@ +{{! + Copyright (c) 2023-Present, Okta, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} +package {{invokerPackage}}.auth; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.MultiValueMap; + +{{>generatedAnnotation}} +public class ApiKeyAuth implements Authentication { +private final String location; +private final String paramName; + +private String apiKey; +private String apiKeyPrefix; + +public ApiKeyAuth(String location, String paramName) { +this.location = location; +this.paramName = paramName; +} + +public String getLocation() { +return location; +} + +public String getParamName() { +return paramName; +} + +public String getApiKey() { +return apiKey; +} + +public void setApiKey(String apiKey) { +this.apiKey = apiKey; +} + +public String getApiKeyPrefix() { +return apiKeyPrefix; +} + +public void setApiKeyPrefix(String apiKeyPrefix) { +this.apiKeyPrefix = apiKeyPrefix; +} + +@Override +public void applyToParams(MultiValueMap queryParams, HttpHeaders headerParams, MultiValueMap cookieParams) { +if (apiKey == null) { +return; +} +String value; +if (apiKeyPrefix != null) { +value = apiKeyPrefix + " " + apiKey; +} else { +value = apiKey; +} +if (location.equals("query")) { +queryParams.add(paramName, value); +} else if (location.equals("header")) { +if (!headerParams.containsKey(paramName) && !headerParams.containsValue(value)) { +headerParams.add(paramName, value); +} +} else if (location.equals("cookie")) { +cookieParams.add(paramName, value); +} +} +} \ No newline at end of file diff --git a/api/src/test/groovy/com/okta/sdk/resource/common/PagedListTest.groovy b/api/src/test/groovy/com/okta/sdk/resource/common/PagedListTest.groovy new file mode 100644 index 00000000000..1f9a342e5cc --- /dev/null +++ b/api/src/test/groovy/com/okta/sdk/resource/common/PagedListTest.groovy @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023-Present, Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.sdk.resource + +import com.okta.sdk.resource.common.PagedList +import org.testng.annotations.Test + +import static org.testng.Assert.assertEquals +import static org.testng.Assert.assertNull +/** + * + * @since 10.1.1 + */ +class PagedListTest { + + @Test + void testGetAfterMarker() { + + String nextPage = "https://example.okta.com/api/v1/users?after=000u6t5aytx1WQUaxw1d7&limit=2" + + PagedList pagedList = new PagedList() + String afterMarker = pagedList.getAfter(nextPage) + + assertEquals(afterMarker, "000u6t5aytx1WQUaxw1d7") + + nextPage = "https://example.okta.com/api/v1/users?x=y" + + afterMarker = pagedList.getAfter(nextPage) + + assertNull(afterMarker) + } +} diff --git a/examples/quickstart/src/main/java/quickstart/Quickstart.java b/examples/quickstart/src/main/java/quickstart/Quickstart.java index 3a835420765..37b75b9e7a8 100644 --- a/examples/quickstart/src/main/java/quickstart/Quickstart.java +++ b/examples/quickstart/src/main/java/quickstart/Quickstart.java @@ -15,6 +15,7 @@ */ package quickstart; +import com.okta.sdk.resource.common.PagedList; import org.openapitools.client.ApiClient; import com.okta.sdk.client.Clients; import com.okta.sdk.client.ClientBuilder; @@ -98,6 +99,9 @@ public static void main(String[] args) { // get the list of users List users = userApi.listUsers(null, null, null, "status eq \"ACTIVE\"", null, null, null); + // get the paginated list of users + PagedList pagedUserList = userApi.listUsersWithPaginationInfo(null, null, 5, "status eq \"ACTIVE\"", null, null, null); + // get the first user in the collection println("First user in collection: " + Objects.requireNonNull(Objects.requireNonNull(users.stream().findFirst().orElse(null)).getProfile()).getEmail()); } diff --git a/examples/quickstart/src/main/java/quickstart/ReadmeSnippets.java b/examples/quickstart/src/main/java/quickstart/ReadmeSnippets.java index 3f616084f07..799de53a59a 100644 --- a/examples/quickstart/src/main/java/quickstart/ReadmeSnippets.java +++ b/examples/quickstart/src/main/java/quickstart/ReadmeSnippets.java @@ -118,7 +118,7 @@ private void listAllUsers() { private void userSearch() { UserApi userApi = new UserApi(client); // search by email - List users = userApi.listUsers(null, null, 5, null, "jcoder@example.com", null, null); + List users = userApi.listUsers(null, null, 5, null, "profile.email eq \"jcoder@example.com\"", null, null); // filter parameter users = userApi.listUsers(null, null, null, "status eq \"ACTIVE\"",null, null, null); @@ -291,24 +291,17 @@ private void paging() { UserApi userApi = new UserApi(client); - // limit - int pageSize = 2; + int limit = 2; - PagedList usersPagedListOne = userApi.listUsersWithPaginationInfo(null, null, pageSize, null, null, null, null); + PagedList pagedUserList = userApi.listUsersWithPaginationInfo(null, null, limit, null, null, null, null); - // e.g. https://example.okta.com/api/v1/users?after=000u3pfv9v4SQXvpBB0g7&limit=2 - String nextPageUrl = usersPagedListOne.getNextPage(); - - // replace 'after' with actual cursor from the nextPageUrl - PagedList usersPagedListTwo = userApi.listUsersWithPaginationInfo("after", null, pageSize, null, null, null, null); - - // loop through all of them (paging is automatic) - for (User tmpUser : usersPagedListOne.getItems()) { + // loop through all of them + for (User tmpUser : pagedUserList.getItems()) { log.info("User: {}", tmpUser.getProfile().getEmail()); } // or stream - usersPagedListOne.getItems().forEach(tmpUser -> log.info("User: {}", tmpUser.getProfile().getEmail())); + pagedUserList.getItems().forEach(tmpUser -> log.info("User: {}", tmpUser.getProfile().getEmail())); } private void complexCaching() { diff --git a/impl/src/main/java/com/okta/sdk/impl/client/DefaultClientBuilder.java b/impl/src/main/java/com/okta/sdk/impl/client/DefaultClientBuilder.java index cb7044a041c..e4d9cec8dfb 100644 --- a/impl/src/main/java/com/okta/sdk/impl/client/DefaultClientBuilder.java +++ b/impl/src/main/java/com/okta/sdk/impl/client/DefaultClientBuilder.java @@ -70,6 +70,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; +import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -431,7 +432,7 @@ private RestTemplate restTemplate(ClientConfiguration clientConfig) { return restTemplate; } - private HttpComponentsClientHttpRequestFactory requestFactory(ClientConfiguration clientConfig) { + private BufferingClientHttpRequestFactory requestFactory(ClientConfiguration clientConfig) { final HttpClientBuilder clientBuilder = HttpClientBuilder.create(); @@ -455,7 +456,7 @@ private HttpComponentsClientHttpRequestFactory requestFactory(ClientConfiguratio clientHttpRequestFactory.setConnectTimeout(clientConfig.getConnectionTimeout() * 1000); clientHttpRequestFactory.setReadTimeout(clientConfig.getConnectionTimeout() * 1000); - return clientHttpRequestFactory; + return new BufferingClientHttpRequestFactory(clientHttpRequestFactory); } @Override diff --git a/integration-tests/src/test/groovy/com/okta/sdk/tests/it/UsersIT.groovy b/integration-tests/src/test/groovy/com/okta/sdk/tests/it/UsersIT.groovy index 710c7559760..cfde5976995 100644 --- a/integration-tests/src/test/groovy/com/okta/sdk/tests/it/UsersIT.groovy +++ b/integration-tests/src/test/groovy/com/okta/sdk/tests/it/UsersIT.groovy @@ -861,27 +861,13 @@ class UsersIT extends ITSupport { UserApi userApi = new UserApi(getClient()) - // limit - int pageSize = 2 + int limit = 2 - PagedList usersPagedListOne = userApi.listUsersWithPaginationInfo(null, null, pageSize, null, null, null, null) + PagedList usersPagedList = userApi.listUsersWithPaginationInfo(null, null, limit, null, null, null, null) - assertThat usersPagedListOne, notNullValue() - assertThat usersPagedListOne.items().size(), is(2) - assertThat usersPagedListOne.self, notNullValue() - assertThat usersPagedListOne.nextPage, notNullValue() - - // e.g. https://example.okta.com/api/v1/users?after=000u3pfv9v4SQXvpBB0g7&limit=2 - String nextPageUrl = usersPagedListOne.nextPage - String after = splitQuery(new URL(nextPageUrl)).get("after") - - assertThat after, notNullValue() - - PagedList usersPagedListTwo = userApi.listUsersWithPaginationInfo(null, after, pageSize, null, null, null, null) - - assertThat usersPagedListTwo, notNullValue() - assertThat usersPagedListTwo.items().size(), is(greaterThanOrEqualTo(1)) - assertThat usersPagedListTwo.self, equalTo(usersPagedListOne.nextPage) + assertThat usersPagedList, notNullValue() + assertThat usersPagedList.self, notNullValue() + assertThat usersPagedList.items().size(), is(greaterThanOrEqualTo(3)) } @Test (groups = "group3") @@ -920,20 +906,4 @@ class UsersIT extends ITSupport { return Base64.getEncoder().encodeToString(bytes) } - /** - * Split a URL with query strings into name value pairs. - * @param url - * @return map of query string name value pairs - * @throws UnsupportedEncodingException - */ - private static Map splitQuery(URL url) throws UnsupportedEncodingException { - Map 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), "UTF-8"), URLDecoder.decode(pair.substring(index + 1), "UTF-8")) - } - return query_pairs - } } \ No newline at end of file