33import com .fasterxml .jackson .databind .JsonNode ;
44import com .fasterxml .jackson .databind .ObjectMapper ;
55import com .twilio .exception .ApiConnectionException ;
6+ import com .twilio .exception .ApiException ;
67import lombok .Getter ;
78
89import java .io .IOException ;
1213public class TokenPaginationPage <T > extends Page <T > {
1314 @ Getter
1415 private final String key ;
15- @ Getter
1616 private final String nextToken ;
17- @ Getter
1817 private final String previousToken ;
1918
2019 private TokenPaginationPage (Builder <T > b ) {
@@ -24,6 +23,16 @@ private TokenPaginationPage(Builder<T> b) {
2423 this .previousToken = b .previousToken ;
2524 }
2625
26+ // adding custom getter and not using lombok to handle null token
27+ // when token is null, lombok getter returns "null" not a null object
28+ public String getNextToken () {
29+ return nextToken ;
30+ }
31+
32+ public String getPreviousToken () {
33+ return previousToken ;
34+ }
35+
2736 @ Override
2837 public String previousQueryString () {
2938 return getQueryString (previousToken );
@@ -34,17 +43,32 @@ public String nextQueryString() {
3443 return getQueryString (nextToken );
3544 }
3645
46+ private void addQueryOperators (StringBuilder query ) {
47+ if (query .length () == 0 ) {
48+ query .append ("?" );
49+ } else {
50+ query .append ("&" );
51+ }
52+ }
53+
3754 private String getQueryString (String pageToken ) {
3855 StringBuilder query = new StringBuilder ();
3956 if (pageSize > 0 ) {
40- query .append ("?pageSize=" ).append (pageSize );
57+ addQueryOperators (query );
58+ query .append ("pageSize=" ).append (pageSize );
4159 }
4260 if (pageToken != null && !pageToken .isEmpty ()) {
43- query .append ("&pageToken=" ).append (pageToken );
61+ addQueryOperators (query );
62+ query .append ("pageToken=" ).append (pageToken );
4463 }
4564 return query .toString ();
4665 }
4766
67+ /**
68+ * Checks if there is a next page of records available.
69+ *
70+ * @return true if a next page is available, false otherwise
71+ */
4872 @ Override
4973 public boolean hasNextPage () {
5074 return (nextToken != null && !nextToken .isEmpty ());
@@ -65,15 +89,19 @@ public static <T> TokenPaginationPage<T> fromJson(String recordKey, String json,
6589 try {
6690 List <T > results = new ArrayList <>();
6791 JsonNode root = mapper .readTree (json );
68- JsonNode meta = root .get ("meta" );
69- String key = meta .get ("key" ).asText ();
70- JsonNode records = root .get (key );
71- for (final JsonNode record : records ) {
72- results .add (mapper .readValue (record .toString (), recordType ));
92+ try {
93+ JsonNode meta = root .get ("meta" );
94+ String key = meta .get ("key" ).asText ();
95+ JsonNode records = root .get (key );
96+ for (final JsonNode record : records ) {
97+ results .add (mapper .readValue (record .toString (), recordType ));
98+ }
99+
100+ return buildPage (meta , results );
101+ } catch (NullPointerException e ) {
102+ throw new ApiException ("Key not found" , e );
73103 }
74104
75- return buildPage (meta , results );
76-
77105 } catch (final IOException e ) {
78106 throw new ApiConnectionException (
79107 "Unable to deserialize response: " + e .getMessage () + "\n JSON: " + json , e
@@ -82,19 +110,30 @@ public static <T> TokenPaginationPage<T> fromJson(String recordKey, String json,
82110 }
83111
84112 private static <T > TokenPaginationPage <T > buildPage (JsonNode meta , List <T > results ) {
85- Builder <T > builder = new Builder <T >()
86- .key (meta .get ("key" ).asText ());
113+ try {
114+ Builder <T > builder = new Builder <T >()
115+ .key (meta .get ("key" ).asText ());
87116
88- builder .nextToken (meta .get ("nextToken" ).asText ());
89- builder .previousToken (meta .get ("previousToken" ).asText ());
117+ JsonNode nextTokenNode = meta .get ("nextToken" );
118+ if (nextTokenNode != null && !nextTokenNode .isNull ()) {
119+ builder .nextToken (nextTokenNode .asText ());
120+ }
121+
122+ JsonNode previousTokenNode = meta .get ("previousToken" );
123+ if (previousTokenNode != null && !previousTokenNode .isNull ()) {
124+ builder .previousToken (previousTokenNode .asText ());
125+ }
90126
91- JsonNode pageSizeNode = meta .get ("pageSize" );
92- builder .pageSize (pageSizeNode .asInt ()); // pageSize is mandatory
127+ JsonNode pageSizeNode = meta .get ("pageSize" );
128+ builder .pageSize (pageSizeNode .asInt ());
93129
94- return builder .records (results ).build ();
130+ return builder .records (results ).build ();
131+ } catch (NullPointerException e ) {
132+ throw new ApiException ("Key not found" , e );
133+ }
95134 }
96135
97- private static class Builder <T > extends Page .Builder <T > {
136+ protected static class Builder <T > extends Page .Builder <T > {
98137 private String key ;
99138 private String nextToken ;
100139 private String previousToken ;
0 commit comments