diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f3bb759..ccba14f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## Next release + +- Adds `allChildren` function in User service to get a paginated list of child users +- Adds `getNextPage` function in User service to get next paginated list of child users + ## v7.0.1 (2023-12-08) - Adds the `object` field to all models; previously, most models were missing this field. diff --git a/src/main/java/com/easypost/model/ChildUserCollection.java b/src/main/java/com/easypost/model/ChildUserCollection.java new file mode 100644 index 000000000..0d99d67ac --- /dev/null +++ b/src/main/java/com/easypost/model/ChildUserCollection.java @@ -0,0 +1,25 @@ +package com.easypost.model; + +import java.util.List; +import java.util.Map; + +import lombok.Getter; + +@Getter +public final class ChildUserCollection extends PaginatedCollection { + private List children; + + @Override + protected Map buildNextPageParameters(List children, Integer pageSize) { + String lastId = children.get(children.size() - 1).getId(); + + Map parameters = new java.util.HashMap<>(); + parameters.put("before_id", lastId); + + if (pageSize != null) { + parameters.put("page_size", pageSize); + } + + return parameters; + } +} diff --git a/src/main/java/com/easypost/service/UserService.java b/src/main/java/com/easypost/service/UserService.java index 25d69df2f..44d67a57f 100644 --- a/src/main/java/com/easypost/service/UserService.java +++ b/src/main/java/com/easypost/service/UserService.java @@ -2,18 +2,23 @@ import com.easypost.Constants; import com.easypost.exception.EasyPostException; +import com.easypost.exception.General.EndOfPaginationError; import com.easypost.exception.General.FilteringError; import com.easypost.http.Requestor; import com.easypost.http.Requestor.RequestMethod; import com.easypost.model.ApiKey; import com.easypost.model.ApiKeys; import com.easypost.model.Brand; +import com.easypost.model.ChildUserCollection; import com.easypost.model.User; +import lombok.SneakyThrows; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; public class UserService { private final EasyPostClient client; @@ -137,4 +142,35 @@ public Brand updateBrand(final String id, final Map params) thro return Requestor.request(RequestMethod.PUT, endpoint, wrappedParams, Brand.class, client); } + + /** + * Retrieve the paginated list of child users for the authenticated user. + * + * @param params Map of parameters. + * @return ChildUserCollection object. + * @throws EasyPostException when the request fails. + */ + public ChildUserCollection allChildren(final Map params) throws EasyPostException { + String endpoint = "users/children"; + + return Requestor.request(RequestMethod.GET, endpoint, params, ChildUserCollection.class, client); + } + + /** + * Get the next page of a ChildUserCollection. + * + * @param collection ChildUserCollection to get next page of. + * @param pageSize The number of results to return on the next page. + * @return ChildUserCollection object. + * @throws EndOfPaginationError when there are no more pages to retrieve. + */ + public ChildUserCollection getNextPage(ChildUserCollection collection, Integer pageSize) + throws EndOfPaginationError { + return collection.getNextPage(new Function, ChildUserCollection>() { + @Override @SneakyThrows + public ChildUserCollection apply(Map parameters) { + return allChildren(parameters); + } + }, collection.getChildren(), pageSize); + } } diff --git a/src/test/cassettes/user/all_children.json b/src/test/cassettes/user/all_children.json new file mode 100644 index 000000000..485cb59ae --- /dev/null +++ b/src/test/cassettes/user/all_children.json @@ -0,0 +1,88 @@ +[ + { + "recordedAt": 1704394018, + "request": { + "body": "", + "method": "GET", + "headers": { + "Accept-Charset": [ + "UTF-8" + ], + "User-Agent": [ + "REDACTED" + ] + }, + "uri": "https://api.easypost.com/v2/users/children?%70%61%67%65%5F%73%69%7A%65\u003d%35" + }, + "response": { + "body": "{\n \"children\": [\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-05-16T22:01:20Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_484dd58db70a4f31b4bb862998cf0e04\",\n \"object\": \"User\"\n },\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-09-27T22:05:26Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_14e894c0d541459395f4456e7cf4f175\",\n \"object\": \"User\"\n },\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-11-30T19:23:22Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_f04df3dad13848339a7975d295d6629f\",\n \"object\": \"User\"\n }\n ],\n \"has_more\": false\n}", + "httpVersion": null, + "headers": { + "null": [ + "HTTP/1.1 200 OK" + ], + "content-length": [ + "673" + ], + "expires": [ + "0" + ], + "x-node": [ + "bigweb34nuq" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-backend": [ + "easypost" + ], + "x-permitted-cross-domain-policies": [ + "none" + ], + "x-download-options": [ + "noopen" + ], + "strict-transport-security": [ + "max-age\u003d31536000; includeSubDomains; preload" + ], + "pragma": [ + "no-cache" + ], + "x-content-type-options": [ + "nosniff" + ], + "x-xss-protection": [ + "1; mode\u003dblock" + ], + "x-ep-request-uuid": [ + "3c78c40b6596fd22e799facc000653b1" + ], + "x-proxied": [ + "extlb2nuq 003ad9bca0", + "intlb2nuq 2c48984abf" + ], + "referrer-policy": [ + "strict-origin-when-cross-origin" + ], + "x-runtime": [ + "0.039539" + ], + "content-type": [ + "application/json; charset\u003dutf-8" + ], + "x-version-label": [ + "easypost-202401041812-437974c716-master" + ], + "cache-control": [ + "private, no-cache, no-store" + ] + }, + "status": { + "code": 200, + "message": "OK" + }, + "uri": "https://api.easypost.com/v2/users/children?%70%61%67%65%5F%73%69%7A%65\u003d%35" + }, + "duration": 420 + } +] \ No newline at end of file diff --git a/src/test/cassettes/user/get_next_page.json b/src/test/cassettes/user/get_next_page.json new file mode 100644 index 000000000..68dfc51cb --- /dev/null +++ b/src/test/cassettes/user/get_next_page.json @@ -0,0 +1,91 @@ +[ + { + "recordedAt": 1704394017, + "request": { + "body": "", + "method": "GET", + "headers": { + "Accept-Charset": [ + "UTF-8" + ], + "User-Agent": [ + "REDACTED" + ] + }, + "uri": "https://api.easypost.com/v2/users/children?%70%61%67%65%5F%73%69%7A%65\u003d%35" + }, + "response": { + "body": "{\n \"children\": [\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-05-16T22:01:20Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_484dd58db70a4f31b4bb862998cf0e04\",\n \"object\": \"User\"\n },\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-09-27T22:05:26Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_14e894c0d541459395f4456e7cf4f175\",\n \"object\": \"User\"\n },\n {\n \"parent_id\": \"user_0f6b83e3530b401cb1e8aeaa6a250d4d\",\n \"name\": \"Test User\",\n \"verified\": true,\n \"created_at\": \"2023-11-30T19:23:22Z\",\n \"phone_number\": \"REDACTED\",\n \"id\": \"user_f04df3dad13848339a7975d295d6629f\",\n \"object\": \"User\"\n }\n ],\n \"has_more\": false\n}", + "httpVersion": null, + "headers": { + "null": [ + "HTTP/1.1 200 OK" + ], + "content-length": [ + "673" + ], + "expires": [ + "0" + ], + "x-node": [ + "bigweb32nuq" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-backend": [ + "easypost" + ], + "x-permitted-cross-domain-policies": [ + "none" + ], + "x-download-options": [ + "noopen" + ], + "strict-transport-security": [ + "max-age\u003d31536000; includeSubDomains; preload" + ], + "pragma": [ + "no-cache" + ], + "x-canary": [ + "direct" + ], + "x-content-type-options": [ + "nosniff" + ], + "x-xss-protection": [ + "1; mode\u003dblock" + ], + "x-ep-request-uuid": [ + "3c78c4086596fd20e799fac90006528a" + ], + "x-proxied": [ + "extlb2nuq 003ad9bca0", + "intlb2nuq 2c48984abf" + ], + "referrer-policy": [ + "strict-origin-when-cross-origin" + ], + "x-runtime": [ + "0.193572" + ], + "content-type": [ + "application/json; charset\u003dutf-8" + ], + "x-version-label": [ + "easypost-202401041812-437974c716-master" + ], + "cache-control": [ + "private, no-cache, no-store" + ] + }, + "status": { + "code": 200, + "message": "OK" + }, + "uri": "https://api.easypost.com/v2/users/children?%70%61%67%65%5F%73%69%7A%65\u003d%35" + }, + "duration": 594 + } +] \ No newline at end of file diff --git a/src/test/java/com/easypost/UserTest.java b/src/test/java/com/easypost/UserTest.java index 4cb51ca3b..b33ee2bcf 100644 --- a/src/test/java/com/easypost/UserTest.java +++ b/src/test/java/com/easypost/UserTest.java @@ -1,19 +1,25 @@ package com.easypost; import com.easypost.exception.EasyPostException; +import com.easypost.exception.General.EndOfPaginationError; import com.easypost.model.Brand; +import com.easypost.model.ChildUserCollection; import com.easypost.model.User; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public final class UserTest { private static String testUserId = null; @@ -177,4 +183,54 @@ public void testUpdateBrand() throws EasyPostException { assertTrue(brand.getId().startsWith("brd_")); assertEquals(color, brand.getColor()); } + + /** + * Test retrieving a paginated list of children. + * + * @throws EasyPostException when the request fails. + */ + @Test + public void testAllChildren() throws EasyPostException { + vcr.setUpTest("all_children"); + + Map params = new HashMap<>(); + params.put("page_size", Fixtures.pageSize()); + + ChildUserCollection children = vcr.client.user.allChildren(params); + + List childrenList = children.getChildren(); + + assertTrue(childrenList.size() <= Fixtures.pageSize()); + assertNotNull(children.getHasMore()); + assertTrue(childrenList.stream().allMatch(children_user -> children_user != null)); + } + + /** + * Test retrieving the next page of child users. + * + * @throws EasyPostException when the request fails. + */ + @Test + public void testGetNextPage() throws EasyPostException { + vcr.setUpTest("get_next_page"); + + Map params = new HashMap<>(); + params.put("page_size", Fixtures.pageSize()); + ChildUserCollection collection = vcr.client.user.allChildren(params); + + try { + ChildUserCollection nextPage = vcr.client.user.getNextPage(collection, Fixtures.pageSize()); + + assertNotNull(nextPage); + + String firstIdOfFirstPage = collection.getChildren().get(0).getId(); + String firstIdOfSecondPage = nextPage.getChildren().get(0).getId(); + + assertNotEquals(firstIdOfFirstPage, firstIdOfSecondPage); + } catch (EndOfPaginationError e) { // There's no next page, that's not a failure + assertTrue(true); + } catch (Exception e) { // Any other exception is a failure + fail(); + } + } }