1
1
package org .zendesk .client .v2 ;
2
2
3
3
import java .util .concurrent .ExecutionException ;
4
- import java .util .concurrent .atomic .AtomicInteger ;
5
-
4
+
6
5
import org .zendesk .client .v2 .model .*;
7
6
import org .zendesk .client .v2 .model .targets .*;
8
7
44
43
import java .util .Iterator ;
45
44
import java .util .List ;
46
45
import java .util .Map ;
47
- import java .util .NoSuchElementException ;
48
-
46
+ import java .util .NoSuchElementException ;
47
+ import java . util . regex . Pattern ;
49
48
50
49
/**
51
50
* @author stephenc
@@ -1103,42 +1102,34 @@ private <T> ListenableFuture<T> submit(Request request, AsyncCompletionHandler<T
1103
1102
}
1104
1103
1105
1104
private Request req (String method , Uri template ) {
1106
- RequestBuilder builder = new RequestBuilder (method );
1107
- if (realm != null ) {
1108
- builder .setRealm (realm );
1109
- } else {
1110
- builder .addHeader ("Authorization" , "Bearer " + oauthToken );
1111
- }
1112
- builder .setUrl (template .toString ());
1113
- return builder .build ();
1105
+ return req (method , template .toString ());
1114
1106
}
1115
1107
1116
- private Request req (String method , Uri template , String contentType , byte [] body ) {
1108
+ private static final Pattern RESTRICTED_PATTERN = Pattern .compile ("%2B" , Pattern .LITERAL );
1109
+
1110
+ private Request req (String method , String url ) {
1117
1111
RequestBuilder builder = new RequestBuilder (method );
1118
1112
if (realm != null ) {
1119
1113
builder .setRealm (realm );
1120
1114
} else {
1121
1115
builder .addHeader ("Authorization" , "Bearer " + oauthToken );
1122
- }
1123
- builder .setUrl (template .toString ());
1124
- builder .addHeader ("Content-type" , contentType );
1125
- builder .setBody (body );
1116
+ }
1117
+ builder .setUrl (RESTRICTED_PATTERN .matcher (url ).replaceAll ("+" )); // replace out %2B with + due to API restriction
1126
1118
return builder .build ();
1127
1119
}
1128
1120
1129
- private Request req (String method , Uri template , int page ) {
1121
+ private Request req (String method , Uri template , String contentType , byte [] body ) {
1130
1122
RequestBuilder builder = new RequestBuilder (method );
1131
1123
if (realm != null ) {
1132
1124
builder .setRealm (realm );
1133
1125
} else {
1134
1126
builder .addHeader ("Authorization" , "Bearer " + oauthToken );
1135
1127
}
1136
- builder .addQueryParameter ("page" , Integer .toString (page ));
1137
- builder .setUrl (template .toString ().replace ("%2B" , "+" )); //replace out %2B with + due to API restriction
1128
+ builder .setUrl (RESTRICTED_PATTERN .matcher (template .toString ()).replaceAll ("+" )); //replace out %2B with + due to API restriction
1129
+ builder .addHeader ("Content-type" , contentType );
1130
+ builder .setBody (body );
1138
1131
return builder .build ();
1139
1132
}
1140
-
1141
-
1142
1133
1143
1134
protected AsyncCompletionHandler <Void > handleStatus () {
1144
1135
return new AsyncCompletionHandler <Void >() {
@@ -1186,56 +1177,56 @@ public T onCompleted(Response response) throws Exception {
1186
1177
};
1187
1178
}
1188
1179
1189
- protected <T > AsyncCompletionHandler <List <T >> handleList (final Class <T > clazz ) {
1190
- return new AsyncCompletionHandler <List <T >>() {
1180
+ private static final String NEXT_PAGE = "next_page" ;
1181
+
1182
+ private abstract class PagedAsyncCompletionHandler <T > extends AsyncCompletionHandler <T > {
1183
+ private String nextPage ;
1184
+
1185
+ public void setPagedProperties (JsonNode responseNode , Class <?> clazz ) {
1186
+ JsonNode node = responseNode .get (NEXT_PAGE );
1187
+ if (node == null ) {
1188
+ throw new NullPointerException (NEXT_PAGE + " property not found, pagination not supported" +
1189
+ (clazz != null ? " for " + clazz .getName () : "" ));
1190
+ }
1191
+ this .nextPage = node .asText ();
1192
+ }
1193
+
1194
+ public String getNextPage () {
1195
+ return nextPage ;
1196
+ }
1197
+ }
1198
+
1199
+ protected <T > PagedAsyncCompletionHandler <List <T >> handleList (final Class <T > clazz , final String name ) {
1200
+ return new PagedAsyncCompletionHandler <List <T >>() {
1201
+
1191
1202
@ Override
1192
1203
public List <T > onCompleted (Response response ) throws Exception {
1193
1204
logResponse (response );
1194
1205
if (isStatus2xx (response )) {
1206
+ JsonNode responseNode = mapper .readTree (response .getResponseBodyAsBytes ());
1207
+ setPagedProperties (responseNode , clazz );
1195
1208
List <T > values = new ArrayList <T >();
1196
- for (JsonNode node : mapper . readTree ( response . getResponseBodyAsStream () )) {
1209
+ for (JsonNode node : responseNode . get ( name )) {
1197
1210
values .add (mapper .convertValue (node , clazz ));
1198
1211
}
1199
1212
return values ;
1200
1213
}
1201
1214
throw new ZendeskResponseException (response );
1202
1215
}
1203
1216
};
1204
- }
1205
-
1206
- protected <T > AsyncCompletionHandler <List <T >> handleList (final Class <T > clazz , final String name ) {
1207
- final AtomicInteger readCount = new AtomicInteger (0 );
1208
- return new AsyncCompletionHandler <List <T >>() {
1209
- @ Override
1210
- public List <T > onCompleted (Response response ) throws Exception {
1211
- logResponse (response );
1212
- if (isStatus2xx (response )) {
1213
- JsonNode responseNode = mapper .readTree (response .getResponseBodyAsBytes ());
1214
- int count = responseNode .get ("count" ).asInt ();
1215
- if (count > readCount .get ()) {
1216
- List <T > values = new ArrayList <T >();
1217
- for (JsonNode node : responseNode .get (name )) {
1218
- values .add (mapper .convertValue (node , clazz ));
1219
- readCount .incrementAndGet ();
1220
- }
1221
- return values ;
1222
- } else {
1223
- return null ;
1224
- }
1225
- }
1226
- throw new ZendeskResponseException (response );
1227
- }
1228
- };
1229
1217
}
1230
-
1231
- protected AsyncCompletionHandler <List <SearchResultEntity >> handleSearchList (final String name ) {
1232
- return new AsyncCompletionHandler <List <SearchResultEntity >>() {
1218
+
1219
+ protected PagedAsyncCompletionHandler <List <SearchResultEntity >> handleSearchList (final String name ) {
1220
+ return new PagedAsyncCompletionHandler <List <SearchResultEntity >>() {
1221
+
1233
1222
@ Override
1234
1223
public List <SearchResultEntity > onCompleted (Response response ) throws Exception {
1235
1224
logResponse (response );
1236
1225
if (isStatus2xx (response )) {
1226
+ JsonNode responseNode = mapper .readTree (response .getResponseBodyAsStream ()).get (name );
1227
+ setPagedProperties (responseNode , null );
1237
1228
List <SearchResultEntity > values = new ArrayList <SearchResultEntity >();
1238
- for (JsonNode node : mapper . readTree ( response . getResponseBodyAsStream ()). get ( name ) ) {
1229
+ for (JsonNode node : responseNode ) {
1239
1230
Class <? extends SearchResultEntity > clazz = searchResultTypes .get (node .get ("result_type" ));
1240
1231
if (clazz != null ) {
1241
1232
values .add (mapper .convertValue (node , clazz ));
@@ -1248,34 +1239,28 @@ public List<SearchResultEntity> onCompleted(Response response) throws Exception
1248
1239
};
1249
1240
}
1250
1241
1251
- protected AsyncCompletionHandler <List <Target >> handleTargetList (final String name ) {
1252
- final AtomicInteger readCount = new AtomicInteger (0 );
1253
- return new AsyncCompletionHandler <List <Target >>() {
1254
- @ Override
1255
- public List <Target > onCompleted (Response response ) throws Exception {
1256
- logResponse (response );
1257
- if (isStatus2xx (response )) {
1258
- JsonNode responseNode = mapper .readTree (response .getResponseBodyAsBytes ());
1259
- int count = responseNode .get ("count" ).asInt ();
1260
- if (count > readCount .get ()) {
1261
- List <Target > values = new ArrayList <Target >();
1262
- for (JsonNode node : responseNode .get (name )) {
1263
- Class <? extends Target > clazz = targetTypes .get (node .get ("type" ).asText ());
1264
- if (clazz != null ) {
1265
- values .add (mapper .convertValue (node , clazz ));
1266
- }
1267
- readCount .incrementAndGet ();
1268
- }
1269
- return values ;
1270
- } else {
1271
- return null ;
1272
- }
1273
- }
1274
- throw new ZendeskResponseException (response );
1275
- }
1276
- };
1277
- }
1242
+ protected PagedAsyncCompletionHandler <List <Target >> handleTargetList (final String name ) {
1243
+ return new PagedAsyncCompletionHandler <List <Target >>() {
1278
1244
1245
+ @ Override
1246
+ public List <Target > onCompleted (Response response ) throws Exception {
1247
+ logResponse (response );
1248
+ if (isStatus2xx (response )) {
1249
+ JsonNode responseNode = mapper .readTree (response .getResponseBodyAsBytes ());
1250
+ setPagedProperties (responseNode , null );
1251
+ List <Target > values = new ArrayList <Target >();
1252
+ for (JsonNode node : responseNode .get (name )) {
1253
+ Class <? extends Target > clazz = targetTypes .get (node .get ("type" ).asText ());
1254
+ if (clazz != null ) {
1255
+ values .add (mapper .convertValue (node , clazz ));
1256
+ }
1257
+ }
1258
+ return values ;
1259
+ }
1260
+ throw new ZendeskResponseException (response );
1261
+ }
1262
+ };
1263
+ }
1279
1264
1280
1265
private TemplateUri tmpl (String template ) {
1281
1266
return new TemplateUri (url + template );
@@ -1431,48 +1416,34 @@ public static ObjectMapper createMapper() {
1431
1416
private class PagedIterable <T > implements Iterable <T > {
1432
1417
1433
1418
private final Uri url ;
1434
- private final AsyncCompletionHandler <List <T >> handler ;
1435
- private final int initialPage ;
1436
- private int size = 0 ;
1419
+ private final PagedAsyncCompletionHandler <List <T >> handler ;
1437
1420
1438
- private PagedIterable (Uri url , AsyncCompletionHandler <List <T >> handler ) {
1439
- this (url , handler , 1 );
1440
- }
1441
-
1442
- private PagedIterable (Uri url , AsyncCompletionHandler <List <T >> handler , int initialPage ) {
1421
+ private PagedIterable (Uri url , PagedAsyncCompletionHandler <List <T >> handler ) {
1443
1422
this .handler = handler ;
1444
1423
this .url = url ;
1445
- this .initialPage = initialPage ;
1446
1424
}
1447
1425
1448
1426
public Iterator <T > iterator () {
1449
- return new PagedIterator (initialPage );
1427
+ return new PagedIterator (url );
1450
1428
}
1451
1429
1452
1430
private class PagedIterator implements Iterator <T > {
1453
1431
1454
1432
private Iterator <T > current ;
1455
- private int page ;
1433
+ private String nextPage ;
1456
1434
1457
- private PagedIterator (int page ) {
1458
- this .page = page ;
1435
+ public PagedIterator (Uri url ) {
1436
+ this .nextPage = url . toString () ;
1459
1437
}
1460
1438
1461
1439
public boolean hasNext () {
1462
1440
if (current == null || !current .hasNext ()) {
1463
- if (page > 0 ) {
1464
- List <T > values = complete (submit (req ("GET" , url , page ++), handler ));
1465
- if (values == null || values .isEmpty ()) {
1466
- page = -1 ;
1467
- } else {
1468
- synchronized (this ) {
1469
- size += values .size ();
1470
- }
1471
- current = values .iterator ();
1472
- }
1473
- } else {
1441
+ if (nextPage == null || nextPage .equalsIgnoreCase ("null" )) {
1474
1442
return false ;
1475
1443
}
1444
+ List <T > values = complete (submit (req ("GET" , nextPage ), handler ));
1445
+ nextPage = handler .getNextPage ();
1446
+ current = values .iterator ();
1476
1447
}
1477
1448
return current .hasNext ();
1478
1449
}
0 commit comments