Skip to content

Commit da544a9

Browse files
committed
Merge pull request #651 from ajkannan/resourcemanager-page
Support paging in Resource Manager
2 parents 1dd2c04 + b9cd1a7 commit da544a9

4 files changed

Lines changed: 84 additions & 16 deletions

File tree

gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,6 @@ public static ProjectListOption pageToken(String pageToken) {
147147
*
148148
* <p>The server can return fewer projects than requested. When there are more results than the
149149
* page size, the server will return a page token that can be used to fetch other results.
150-
* Note: pagination is not yet supported; the server currently ignores this field and returns
151-
* all results.
152150
*/
153151
public static ProjectListOption pageSize(int pageSize) {
154152
return new ProjectListOption(ResourceManagerRpc.Option.PAGE_SIZE, pageSize);
@@ -228,8 +226,7 @@ public static ProjectListOption fields(ProjectField... fields) {
228226
*
229227
* <p>This method returns projects in an unspecified order. New projects do not necessarily appear
230228
* at the end of the list. Use {@link ProjectListOption} to filter this list, set page size, and
231-
* set page tokens. Note that pagination is currently not implemented by the Cloud Resource
232-
* Manager API.
229+
* set page tokens.
233230
*
234231
* @see <a
235232
* href="https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects/list">Cloud

gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import java.util.Map;
3535
import java.util.Random;
3636
import java.util.Set;
37-
import java.util.concurrent.ConcurrentHashMap;
37+
import java.util.concurrent.ConcurrentSkipListMap;
3838
import java.util.logging.Level;
3939
import java.util.logging.Logger;
4040
import java.util.zip.GZIPInputStream;
@@ -71,7 +71,7 @@ public class LocalResourceManagerHelper {
7171
ImmutableSet.of('-', '\'', '"', ' ', '!');
7272

7373
private final HttpServer server;
74-
private final ConcurrentHashMap<String, Project> projects = new ConcurrentHashMap<>();
74+
private final ConcurrentSkipListMap<String, Project> projects = new ConcurrentSkipListMap<>();
7575
private final int port;
7676

7777
private static class Response {
@@ -228,7 +228,7 @@ private static String[] parseFields(String query) {
228228
return null;
229229
}
230230

231-
private static Map<String, Object> parseListOptions(String query) {
231+
private static Map<String, Object> parseListOptions(String query) throws IOException {
232232
Map<String, Object> options = new HashMap<>();
233233
if (query != null) {
234234
String[] args = query.split("&");
@@ -245,10 +245,14 @@ private static Map<String, Object> parseListOptions(String query) {
245245
options.put("filter", argEntry[1].split(" "));
246246
break;
247247
case "pageToken":
248-
// support pageToken when Cloud Resource Manager supports this (#421)
248+
options.put("pageToken", argEntry[1]);
249249
break;
250250
case "pageSize":
251-
// support pageSize when Cloud Resource Manager supports this (#421)
251+
int pageSize = Integer.valueOf(argEntry[1]);
252+
if (pageSize < 1) {
253+
throw new IOException("Page size must be greater than 0.");
254+
}
255+
options.put("pageSize", pageSize);
252256
break;
253257
}
254258
}
@@ -353,16 +357,28 @@ Response get(String projectId, String[] fields) {
353357
}
354358

355359
Response list(Map<String, Object> options) {
356-
// Use pageSize and pageToken options when Cloud Resource Manager does so (#421)
357360
List<String> projectsSerialized = new ArrayList<>();
358361
String[] filters = (String[]) options.get("filter");
359362
if (filters != null && !isValidFilter(filters)) {
360363
return Error.INVALID_ARGUMENT.response("Could not parse the filter.");
361364
}
362365
String[] fields = (String[]) options.get("fields");
363-
for (Project p : projects.values()) {
366+
int count = 0;
367+
String pageToken = (String) options.get("pageToken");
368+
Integer pageSize = (Integer) options.get("pageSize");
369+
String nextPageToken = null;
370+
Map<String, Project> projectsToScan = projects;
371+
if (pageToken != null) {
372+
projectsToScan = projects.tailMap(pageToken);
373+
}
374+
for (Project p : projectsToScan.values()) {
375+
if (pageSize != null && count >= pageSize) {
376+
nextPageToken = p.getProjectId();
377+
break;
378+
}
364379
boolean includeProject = includeProject(p, filters);
365380
if (includeProject) {
381+
count++;
366382
try {
367383
projectsSerialized.add(jsonFactory.toString(extractFields(p, fields)));
368384
} catch (IOException e) {
@@ -374,7 +390,13 @@ Response list(Map<String, Object> options) {
374390
StringBuilder responseBody = new StringBuilder();
375391
responseBody.append("{\"projects\": [");
376392
Joiner.on(",").appendTo(responseBody, projectsSerialized);
377-
responseBody.append("]}");
393+
responseBody.append(']');
394+
if (nextPageToken != null) {
395+
responseBody.append(", \"nextPageToken\": \"");
396+
responseBody.append(nextPageToken);
397+
responseBody.append('"');
398+
}
399+
responseBody.append('}');
378400
return new Response(HTTP_OK, responseBody.toString());
379401
}
380402

gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.junit.Test;
2020

2121
import java.util.HashMap;
22+
import java.util.Iterator;
2223
import java.util.Map;
2324

2425
public class LocalResourceManagerHelperTest {
@@ -278,7 +279,7 @@ public void testGetWithOptions() {
278279
public void testList() {
279280
Tuple<String, Iterable<com.google.api.services.cloudresourcemanager.model.Project>> projects =
280281
rpc.list(EMPTY_RPC_OPTIONS);
281-
assertNull(projects.x()); // change this when #421 is resolved
282+
assertNull(projects.x());
282283
assertFalse(projects.y().iterator().hasNext());
283284
rpc.create(COMPLETE_PROJECT);
284285
RESOURCE_MANAGER_HELPER.changeLifecycleState(
@@ -296,12 +297,43 @@ public void testList() {
296297
}
297298
}
298299

300+
@Test
301+
public void testInvalidListPaging() {
302+
Map<ResourceManagerRpc.Option, Object> rpcOptions = new HashMap<>();
303+
rpcOptions.put(ResourceManagerRpc.Option.PAGE_SIZE, -1);
304+
try {
305+
rpc.list(rpcOptions);
306+
} catch (ResourceManagerException e) {
307+
assertEquals("Page size must be greater than 0.", e.getMessage());
308+
}
309+
}
310+
311+
@Test
312+
public void testListPaging() {
313+
Map<ResourceManagerRpc.Option, Object> rpcOptions = new HashMap<>();
314+
rpcOptions.put(ResourceManagerRpc.Option.PAGE_SIZE, 1);
315+
rpc.create(PARTIAL_PROJECT);
316+
rpc.create(COMPLETE_PROJECT);
317+
Tuple<String, Iterable<com.google.api.services.cloudresourcemanager.model.Project>> projects =
318+
rpc.list(rpcOptions);
319+
assertNotNull(projects.x());
320+
Iterator<com.google.api.services.cloudresourcemanager.model.Project> iterator =
321+
projects.y().iterator();
322+
compareReadWriteFields(COMPLETE_PROJECT, iterator.next());
323+
assertFalse(iterator.hasNext());
324+
rpcOptions = new HashMap<>();
325+
rpcOptions.put(ResourceManagerRpc.Option.PAGE_TOKEN, projects.x());
326+
projects = rpc.list(rpcOptions);
327+
iterator = projects.y().iterator();
328+
compareReadWriteFields(PARTIAL_PROJECT, iterator.next());
329+
assertFalse(iterator.hasNext());
330+
assertNull(projects.x());
331+
}
332+
299333
@Test
300334
public void testListFieldOptions() {
301335
Map<ResourceManagerRpc.Option, Object> rpcOptions = new HashMap<>();
302336
rpcOptions.put(ResourceManagerRpc.Option.FIELDS, "projects(projectId,name,labels)");
303-
rpcOptions.put(ResourceManagerRpc.Option.PAGE_TOKEN, "somePageToken");
304-
rpcOptions.put(ResourceManagerRpc.Option.PAGE_SIZE, 1);
305337
rpc.create(PROJECT_WITH_PARENT);
306338
Tuple<String, Iterable<com.google.api.services.cloudresourcemanager.model.Project>> projects =
307339
rpc.list(rpcOptions);

gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.junit.Test;
4343
import org.junit.rules.ExpectedException;
4444

45+
import java.util.Iterator;
4546
import java.util.Map;
4647

4748
public class ResourceManagerImplTest {
@@ -166,7 +167,7 @@ public void testGetWithOptions() {
166167
@Test
167168
public void testList() {
168169
Page<Project> projects = RESOURCE_MANAGER.list();
169-
assertFalse(projects.values().iterator().hasNext()); // TODO: change this when #421 is resolved
170+
assertFalse(projects.values().iterator().hasNext());
170171
RESOURCE_MANAGER.create(PARTIAL_PROJECT);
171172
RESOURCE_MANAGER.create(COMPLETE_PROJECT);
172173
for (Project p : RESOURCE_MANAGER.list().values()) {
@@ -181,6 +182,22 @@ public void testList() {
181182
}
182183
}
183184

185+
@Test
186+
public void testListPaging() {
187+
RESOURCE_MANAGER.create(PARTIAL_PROJECT);
188+
RESOURCE_MANAGER.create(COMPLETE_PROJECT);
189+
Page<Project> page = RESOURCE_MANAGER.list(ProjectListOption.pageSize(1));
190+
assertNotNull(page.nextPageCursor());
191+
Iterator<Project> iterator = page.values().iterator();
192+
compareReadWriteFields(COMPLETE_PROJECT, iterator.next());
193+
assertFalse(iterator.hasNext());
194+
page = page.nextPage();
195+
iterator = page.values().iterator();
196+
compareReadWriteFields(PARTIAL_PROJECT, iterator.next());
197+
assertFalse(iterator.hasNext());
198+
assertNull(page.nextPageCursor());
199+
}
200+
184201
@Test
185202
public void testListFieldOptions() {
186203
RESOURCE_MANAGER.create(COMPLETE_PROJECT);

0 commit comments

Comments
 (0)