diff --git a/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java b/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java index a2b92d752eaf..5a46d058648d 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java @@ -43,6 +43,8 @@ public interface FieldSelector { */ class Helper { + private static final String[] EMPTY_FIELDS = {}; + private Helper() {} private static final Function FIELD_TO_STRING_FUNCTION = @@ -63,18 +65,18 @@ private static String selector(List required, FieldSele } /** - * Returns a composite selector given a number of fields. The string selector returned by this - * method can be used for field selection in API calls that return a single resource. This - * method is not supposed to be used directly by users. + * Returns a composite selector given a number of resource fields. The string selector returned + * by this method can be used for field selection in API calls that return a single resource. + * This method is not supposed to be used directly by users. */ public static String selector(List required, FieldSelector... others) { return selector(required, others, new String[]{}); } /** - * Returns a composite selector given a number of fields and a container name. The string - * selector returned by this method can be used for field selection in API calls that return a - * list of resources. This method is not supposed to be used directly by users. + * Returns a composite selector given a number of resource fields and a container name. The + * string selector returned by this method can be used for field selection in API calls that + * return a list of resources. This method is not supposed to be used directly by users. */ public static String listSelector(String containerName, List required, FieldSelector... others) { @@ -82,14 +84,30 @@ public static String listSelector(String containerName, List required, FieldSelector[] others, String... extraResourceFields) { - return "nextPageToken," + containerName + '(' + return listSelector(EMPTY_FIELDS, containerName, required, others, extraResourceFields); + } + + /** + * Returns a composite selector given a number of top level fields as strings, a number of + * resource fields and a container name. This method also takes an {@code extraResourceFields} + * parameter to specify some extra resource fields as strings. The string selector returned by + * this method can be used for field selection in API calls that return a list of resources. + * This method is not supposed to be used directly by users. + */ + public static String listSelector(String[] topLevelFields, String containerName, + List required, FieldSelector[] others, + String... extraResourceFields) { + Set topLevelStrings = Sets.newHashSetWithExpectedSize(topLevelFields.length + 1); + topLevelStrings.addAll(Lists.asList("nextPageToken", topLevelFields)); + return Joiner.on(',').join(topLevelStrings) + "," + containerName + '(' + selector(required, others, extraResourceFields) + ')'; } } diff --git a/gcloud-java-core/src/test/java/com/google/cloud/FieldSelectorHelperTest.java b/gcloud-java-core/src/test/java/com/google/cloud/FieldSelectorHelperTest.java index 9aa892c7b0b0..02d5847946e0 100644 --- a/gcloud-java-core/src/test/java/com/google/cloud/FieldSelectorHelperTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/FieldSelectorHelperTest.java @@ -46,6 +46,7 @@ public String selector() { return "field3"; } }; + private static final String[] FIRST_LEVEL_FIELDS = {"firstLevel1", "firstLevel2"}; private static final List REQUIRED_FIELDS = ImmutableList.of(FIELD1, FIELD2); private static final String CONTAINER = "container"; @@ -81,4 +82,20 @@ public void testListSelectorWithExtraFields() { assertTrue(selector.endsWith(")")); assertEquals(52, selector.length()); } + + @Test + public void testListSelectorWithFirstLevelFields() { + String selector = Helper.listSelector(FIRST_LEVEL_FIELDS, CONTAINER, REQUIRED_FIELDS, + new FieldSelector[]{FIELD3}, "field4"); + assertTrue(selector.contains("firstLevel1")); + assertTrue(selector.contains("firstLevel2")); + assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.contains("container(")); + assertTrue(selector.contains("field1")); + assertTrue(selector.contains("field2")); + assertTrue(selector.contains("field3")); + assertTrue(selector.contains("field4")); + assertTrue(selector.endsWith(")")); + assertEquals(76, selector.length()); + } } diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java index e15a3ad0077c..4c32aea1a428 100644 --- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -654,6 +654,7 @@ public static BucketListOption fields(BucketField... fields) { */ class BlobListOption extends Option { + private static final String[] TOP_LEVEL_FIELDS = {"prefixes"}; private static final long serialVersionUID = 9083383524788661294L; private BlobListOption(StorageRpc.Option option, Object value) { @@ -713,7 +714,7 @@ public static BlobListOption versions(boolean versions) { */ public static BlobListOption fields(BlobField... fields) { return new BlobListOption(StorageRpc.Option.FIELDS, - Helper.listSelector("items", BlobField.REQUIRED_FIELDS, fields)); + Helper.listSelector(TOP_LEVEL_FIELDS, "items", BlobField.REQUIRED_FIELDS, fields)); } } diff --git a/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 208988a74668..47f776458876 100644 --- a/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -584,12 +584,13 @@ public void testListBucketsWithSelectedFields() { initializeService(); ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); Page page = storage.list(BUCKET_LIST_FIELDS); - String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); - assertTrue(selector.contains("items")); + String selector = (String) capturedOptions.getValue().get(BUCKET_LIST_FIELDS.rpcOption()); + assertTrue(selector.contains("items(")); assertTrue(selector.contains("name")); assertTrue(selector.contains("acl")); assertTrue(selector.contains("location")); assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); assertEquals(38, selector.length()); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), Bucket.class)); @@ -607,10 +608,11 @@ public void testListBucketsWithEmptyFields() { initializeService(); ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); Page page = storage.list(BUCKET_LIST_EMPTY_FIELDS); - String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); - assertTrue(selector.contains("items")); + String selector = (String) capturedOptions.getValue().get(BUCKET_LIST_EMPTY_FIELDS.rpcOption()); + assertTrue(selector.contains("items(")); assertTrue(selector.contains("name")); assertTrue(selector.contains("nextPageToken")); + assertTrue(selector.endsWith(")")); assertEquals(25, selector.length()); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), Bucket.class)); @@ -679,13 +681,15 @@ public void testListBlobsWithSelectedFields() { assertEquals(BLOB_LIST_PREFIX.value(), capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption())); String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); - assertTrue(selector.contains("items")); + assertTrue(selector.contains("prefixes")); + assertTrue(selector.contains("items(")); assertTrue(selector.contains("bucket")); assertTrue(selector.contains("name")); assertTrue(selector.contains("contentType")); assertTrue(selector.contains("md5Hash")); assertTrue(selector.contains("nextPageToken")); - assertEquals(52, selector.length()); + assertTrue(selector.endsWith(")")); + assertEquals(61, selector.length()); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), Blob.class)); } @@ -710,11 +714,13 @@ public void testListBlobsWithEmptyFields() { assertEquals(BLOB_LIST_PREFIX.value(), capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption())); String selector = (String) capturedOptions.getValue().get(BLOB_LIST_EMPTY_FIELDS.rpcOption()); - assertTrue(selector.contains("items")); + assertTrue(selector.contains("prefixes")); + assertTrue(selector.contains("items(")); assertTrue(selector.contains("bucket")); assertTrue(selector.contains("name")); assertTrue(selector.contains("nextPageToken")); - assertEquals(32, selector.length()); + assertTrue(selector.endsWith(")")); + assertEquals(41, selector.length()); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), Blob.class)); }