From ad8ea4d841cc296aeae37415556a857c4a1ba12d Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Wed, 2 Dec 2015 14:36:18 -0800 Subject: [PATCH 1/3] ResourceManagerImpl + docs --- README.md | 36 ++ TESTING.md | 46 +++ gcloud-java-resourcemanager/README.md | 143 +++++++- gcloud-java-resourcemanager/pom.xml | 6 + .../gcloud/resourcemanager/ProjectInfo.java | 3 +- .../resourcemanager/ResourceManagerImpl.java | 231 +++++++++++++ .../ResourceManagerOptions.java | 17 +- .../testing/LocalResourceManagerHelper.java | 5 +- .../LocalResourceManagerHelperTest.java | 16 +- .../ResourceManagerImplTest.java | 322 ++++++++++++++++++ 10 files changed, 806 insertions(+), 19 deletions(-) create mode 100644 gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java create mode 100644 gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java diff --git a/README.md b/README.md index f6104e64a9d3..f8c04e2ffc57 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This client supports the following Google Cloud Platform services: - [Google Cloud Datastore] (#google-cloud-datastore) - [Google Cloud Storage] (#google-cloud-storage) +- [Google Cloud Resource Manager] (#google-cloud-resource-manager) > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. @@ -182,6 +183,38 @@ if (blob == null) { } ``` +Google Cloud Resource Manager +---------------------- + +- [API Documentation][resourcemanager-api] +- [Official Documentation][cloud-resourcemanager-docs] + +#### Preview + +Here is a code snippet showing a simple usage example. Note that you must supply Google SDK credentials for this service, not other forms of authentication listed in the [Authentication section](#authentication). + +```java +import com.google.common.collect.ImmutableMap; +import com.google.gcloud.resourcemanager.ProjectInfo; +import com.google.gcloud.resourcemanager.ResourceManager; +import com.google.gcloud.resourcemanager.ResourceManagerOptions; + +import java.util.Iterator; + +ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service(); +ProjectInfo myProject = resourceManager.get("some-project-id-that-I-own"); +ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() + .labels(ImmutableMap.of("launch-status", "in-development")).build()); +System.out.println("Updated the labels of project " + newProjectInfo.projectId() + + " to be " + newProjectInfo.labels() + System.lineSeparator()); +// List all the projects you have permission to view. +Iterator projectIterator = resourceManager.list().iterateAll(); +System.out.println("Projects I can view:"); +while (projectIterator.hasNext()) { + System.out.println(projectIterator.next().projectId()); +} +``` + Troubleshooting --------------- @@ -241,3 +274,6 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets [cloud-storage-activation]: https://cloud.google.com/storage/docs/signup [storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/storage/package-summary.html + +[resourcemanager-api]:http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/resourcemanager/package-summary.html +[cloud-resourcemanager-docs]:https://cloud.google.com/resource-manager/ diff --git a/TESTING.md b/TESTING.md index 02a3d14ab0bf..d6edd000e6d6 100644 --- a/TESTING.md +++ b/TESTING.md @@ -65,5 +65,51 @@ Here is an example that clears the bucket created in Step 3 with a timeout of 5 RemoteGcsHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS); ``` +### Testing code that uses Resource Manager + +#### On your machine + +You can test against a temporary local Resource Manager by following these steps: + +1. Before running your testing code, start the Resource Manager emulator `LocalResourceManagerHelper`. This can be done as follows: + + ```java + import com.google.gcloud.resourcemanager.testing.LocalResourceManagerHelper; + + LocalResourceManagerHelper helper = LocalResourceManagerHelper.create(); + helper.start(); + ``` + + This will spawn a server thread that listens to `localhost` at an ephemeral port for Resource Manager requests. + +2. In your program, create and use a Resource Manager service object whose host is set host to `localhost` at the appropriate port. For example: + + ```java + ResourceManager resourceManager = ResourceManagerOptions.builder() + .host("http://localhost:" + helper.port()).build().service(); + ``` + +3. Run your tests. + +4. Stop the Resource Manager emulator. + + ```java + helper.stop(); + ``` + + This method will block a short amount of time until the server thread has been terminated. + +#### On a remote machine + +You can test against a remote Resource Manager emulator as well. To do this, set the host to the hostname of the remote machine, like the example below. + + ```java + ResourceManager resourceManager = ResourceManagerOptions.builder() + .host("http://:").build(); + ``` + +Note that the remote Resource Manager emulator must be running before your tests are run. + + [cloud-platform-storage-authentication]:https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts diff --git a/gcloud-java-resourcemanager/README.md b/gcloud-java-resourcemanager/README.md index 11c505c14920..2a229720536c 100644 --- a/gcloud-java-resourcemanager/README.md +++ b/gcloud-java-resourcemanager/README.md @@ -27,27 +27,153 @@ Example Application Authentication -------------- -See the [Authentication](https://github.com/GoogleCloudPlatform/gcloud-java#authentication) section in the base directory's README. +Unlike other `gcloud-java` service libraries, `gcloud-java-resourcemanager` only accepts Google Cloud SDK credentials at this time. If you are having trouble authenticating, it may be that you have other types of credentials that override your Google Cloud SDK credentials. See more about Google Cloud SDK credentials and credential precedence in the global README's [Authentication section](https://github.com/GoogleCloudPlatform/gcloud-java#authentication). About Google Cloud Resource Manager ----------------------------------- -Google [Cloud Resource Manager][cloud-resourcemanager] provides a programmatic way to manage your Google Cloud Platform projects. Google Cloud Resource Manager is currently in beta and may occasionally make backwards incompatible changes. +Google [Cloud Resource Manager][cloud-resourcemanager] provides a programmatic way to manage your Google Cloud Platform projects. With this API, you can do the following: + +* Get a list of all projects associated with an account. +* Create new projects. +* Update existing projects. +* Delete projects. +* Undelete, or recover, projects that you don't want to delete. + +Google Cloud Resource Manager is currently in beta and may occasionally make backwards incompatible changes. Be sure to activate the Google Cloud Resource Manager API on the Developer's Console to use Resource Manager from your project. See the ``gcloud-java`` API [Resource Manager documentation][resourcemanager-api] to learn how to interact with the Cloud Resource Manager using this client Library. - +Getting Started +--------------- +#### Prerequisites +You will also need to set up the local development environment by [installing the Google Cloud SDK](https://cloud.google.com/sdk/) and running the following commands in command line: `gcloud auth login`. + +> Note: You don't need a project ID to use this service. If you have a project ID set in the Google Cloud SDK, you can unset it by typing `gcloud config unset project` in command line. + +#### Installation and setup +You'll need to obtain the `gcloud-java-resourcemanager` library. See the [Quickstart](#quickstart) section to add `gcloud-java-resourcemanager` as a dependency in your code. + +#### Creating an authorized service object +To make authenticated requests to Google Cloud Resource Manager, you must create a service object with Google Cloud SDK credentials. You can then make API calls by calling methods on the Resource Manager service object. The simplest way to authenticate is to use [Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials). These credentials are automatically inferred from your environment, so you only need the following code to create your service object: + +```java +import com.google.gcloud.resourcemanager.ResourceManager; +import com.google.gcloud.resourcemanager.ResourceManagerOptions; + +ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service(); +``` + +#### Creating a project +All you need to create a project is a globally unique project ID. You can also optionally attach a non-unique name and labels to your project. Read more about naming guidelines for project IDs, names, and labels [here](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects). To create a project, add the following imports at the top of your file: + +```java +import com.google.gcloud.resourcemanager.ProjectInfo; +``` + +Then add the following code to create a project (be sure to change `myProjectId` to be something unique). + +```java +String myProjectId = "my-globally-unique-project-id"; // Change to a unique project ID. +ProjectInfo myProject = resourceManager.create(ProjectInfo.builder(myProjectId).build()); +``` + +Note that the return value from `create` is a `ProjectInfo` that includes additional read-only information, like creation time, project number, and lifecycle state. Read more about these fields on the [Projects page](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects). + +#### Getting a specific project +You can load a project if you know it's project ID and have read permissions to the project. For example, say we wanted to get the project we just created. We can do the following: + +```java +ProjectInfo projectFromServer = resourceManager.get(myProjectId); +``` + +#### Editing a project +To edit a project, create a new `ProjectInfo` object and pass it in to the `ResourceManager.replace` method. + +Suppose that you want to add a label for the newly created project to denote that it's launch status is "in development". Import the following: + +```java +import com.google.common.collect.ImmutableMap; +``` + +Then add the following code to your program: + +```java +ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() + .labels(ImmutableMap.of("launch-status", "in-development")).build()); +``` + +Note that the values of the project you pass in to `replace` overwrite the server's values for non-read-only fields, namely `projectName` and `labels`. For example, if you create a project with `projectName` "some-project-name" and subsequently call replace using a `ProjectInfo` object that didn't set the `projectName`, then the server will unset the project's name. The server ignores any attempted changes to the read-only fields `projectNumber`, `lifecycleState`, and `createTime`. The `projectId` cannot change. + +#### Listing all projects +Suppose that we want list of all projects for which we have read permissions. Add the following import: + +```java +import java.util.Iterator; +``` + +Then add the following code to print a list of projects you can view: + +```java +Iterator projectIterator = resourceManager.list().iterateAll(); +System.out.println("Projects I can view:"); +while (projectIterator.hasNext()) { + System.out.println(projectIterator.next().projectId()); +} +``` + +#### Complete source code + +Here we put together all the code shown above into one program. This program assumes that you are running from your own desktop. + +```java +import com.google.common.collect.ImmutableMap; +import com.google.gcloud.resourcemanager.ProjectInfo; +import com.google.gcloud.resourcemanager.ResourceManager; +import com.google.gcloud.resourcemanager.ResourceManagerOptions; + +import java.util.Iterator; + +public class GcloudJavaResourceManagerExample { + + public static void main(String[] args) { + // Create Resource Manager service object. + // By default, credentials are inferred from the runtime environment. + ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service(); + + // Create a project. + String myProjectId = "my-globally-unique-project-id"; // Change to a unique project ID. + ProjectInfo myProject = resourceManager.create(ProjectInfo.builder(myProjectId).build()); + + // Get a project from the server. + ProjectInfo projectFromServer = resourceManager.get(myProjectId); + System.out.println("Got project " + projectFromServer.projectId() + " from the server." + + System.lineSeparator()); + + // Update a project + ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() + .labels(ImmutableMap.of("launch-status", "in-development")).build()); + System.out.println("Updated the labels of project " + newProjectInfo.projectId() + + " to be " + newProjectInfo.labels() + System.lineSeparator()); + + // List all the projects you have permission to view. + Iterator projectIterator = resourceManager.list().iterateAll(); + System.out.println("Projects I can view:"); + while (projectIterator.hasNext()) { + System.out.println(projectIterator.next().projectId()); + } + } +} +``` Java Versions ------------- Java 7 or above is required for using this client. - - Versioning ---------- @@ -57,6 +183,13 @@ It is currently in major version zero (``0.y.z``), which means that anything may change at any time and the public API should not be considered stable. +Testing +------- + +This library has tools to help write tests for code that uses Resource Manager. + +See [TESTING] to read more about testing. + Contributing ------------ diff --git a/gcloud-java-resourcemanager/pom.xml b/gcloud-java-resourcemanager/pom.xml index 515a5c50e04f..70a1b2148c46 100644 --- a/gcloud-java-resourcemanager/pom.xml +++ b/gcloud-java-resourcemanager/pom.xml @@ -27,6 +27,12 @@ google-api-services-cloudresourcemanager v1beta1-rev6-1.19.0 compile + + + com.google.guava + guava-jdk5 + + junit diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java index 5d490e662df0..8e731ecfdbf9 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java @@ -31,7 +31,6 @@ /** * A Google Cloud Resource Manager project metadata object. - * * A Project is a high-level Google Cloud Platform entity. It is a container for ACLs, APIs, * AppEngine Apps, VMs, and other Google Cloud Platform resources. */ @@ -324,7 +323,7 @@ com.google.api.services.cloudresourcemanager.model.Project toPb() { projectPb.setLifecycleState(state.toString()); } if (createTimeMillis != null) { - projectPb.setCreateTime(ISODateTimeFormat.dateTime().print(createTimeMillis)); + projectPb.setCreateTime(ISODateTimeFormat.dateTime().withZoneUTC().print(createTimeMillis)); } if (parent != null) { projectPb.setParent(parent.toPb()); diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java new file mode 100644 index 000000000000..3f01e44c3b52 --- /dev/null +++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java @@ -0,0 +1,231 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.resourcemanager; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.gcloud.RetryHelper.runWithRetries; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.gcloud.BaseService; +import com.google.gcloud.ExceptionHandler; +import com.google.gcloud.ExceptionHandler.Interceptor; +import com.google.gcloud.Page; +import com.google.gcloud.PageImpl; +import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.gcloud.spi.ResourceManagerRpc; +import com.google.gcloud.spi.ResourceManagerRpc.Tuple; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.Callable; + +public class ResourceManagerImpl + extends BaseService implements ResourceManager { + + private static final Interceptor EXCEPTION_HANDLER_INTERCEPTOR = new Interceptor() { + + @Override + public RetryResult afterEval(Exception exception, RetryResult retryResult) { + return Interceptor.RetryResult.CONTINUE_EVALUATION; + } + + @Override + public RetryResult beforeEval(Exception exception) { + if (exception instanceof ResourceManagerException) { + boolean retriable = ((ResourceManagerException) exception).retryable(); + return retriable ? Interceptor.RetryResult.RETRY : Interceptor.RetryResult.NO_RETRY; + } + return Interceptor.RetryResult.CONTINUE_EVALUATION; + } + }; + static final ExceptionHandler EXCEPTION_HANDLER = ExceptionHandler.builder() + .abortOn(RuntimeException.class) + .interceptor(EXCEPTION_HANDLER_INTERCEPTOR) + .build(); + + private final ResourceManagerRpc resourceManagerRpc; + + ResourceManagerImpl(ResourceManagerOptions options) { + super(options); + resourceManagerRpc = options.rpc(); + } + + @Override + public ProjectInfo create(ProjectInfo project) { + final com.google.api.services.cloudresourcemanager.model.Project projectPb = project.toPb(); + try { + return ProjectInfo.fromPb(runWithRetries( + new Callable() { + @Override + public com.google.api.services.cloudresourcemanager.model.Project call() { + return resourceManagerRpc.create(projectPb); + } + }, options().retryParams(), EXCEPTION_HANDLER)); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + @Override + public void delete(final String projectId) { + try { + runWithRetries(new Callable() { + @Override + public Void call() { + resourceManagerRpc.delete(projectId); + return null; + } + }, options().retryParams(), EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + @Override + public ProjectInfo get(final String projectId, ProjectGetOption... options) { + final Map optionsMap = optionMap(options); + try { + return ProjectInfo.fromPb(runWithRetries( + new Callable() { + @Override + public com.google.api.services.cloudresourcemanager.model.Project call() { + return resourceManagerRpc.get(projectId, optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER)); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + private abstract static class BasePageFetcher + implements PageImpl.NextPageFetcher { + protected final Map requestOptions; + protected final ResourceManagerOptions serviceOptions; + + BasePageFetcher(ResourceManagerOptions serviceOptions, String cursor, + Map optionMap) { + this.serviceOptions = serviceOptions; + ImmutableMap.Builder builder = ImmutableMap.builder(); + if (cursor != null) { + builder.put(ResourceManagerRpc.Option.PAGE_TOKEN, cursor); + } + for (Map.Entry option : optionMap.entrySet()) { + if (option.getKey() != ResourceManagerRpc.Option.PAGE_TOKEN) { + builder.put(option.getKey(), option.getValue()); + } + } + this.requestOptions = builder.build(); + } + } + + private static class ProjectPageFetcher extends BasePageFetcher { + ProjectPageFetcher(ResourceManagerOptions serviceOptions, String cursor, + Map optionMap) { + super(serviceOptions, cursor, optionMap); + } + + @Override + public Page nextPage() { + return listProjects(serviceOptions, requestOptions); + } + } + + @Override + public Page list(ProjectListOption... options) { + return listProjects(options(), optionMap(options)); + } + + private static Page listProjects( + final ResourceManagerOptions serviceOptions, + final Map optionsMap) { + try { + Tuple> result = + runWithRetries( + new Callable>>() { + @Override + public Tuple> + call() { + return serviceOptions.rpc().list(optionsMap); + } + }, + serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.x(); + Iterable projects = + result.y() == null + ? ImmutableList.of() : Iterables.transform( + result.y(), + new Function() { + @Override + public ProjectInfo apply( + com.google.api.services.cloudresourcemanager.model.Project projectPb) { + return ProjectInfo.fromPb(projectPb); + } + }); + return new PageImpl<>( + new ProjectPageFetcher(serviceOptions, cursor, optionsMap), cursor, projects); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + @Override + public ProjectInfo replace(ProjectInfo newProject) { + final com.google.api.services.cloudresourcemanager.model.Project projectPb = newProject.toPb(); + try { + return ProjectInfo.fromPb(runWithRetries( + new Callable() { + @Override + public com.google.api.services.cloudresourcemanager.model.Project call() { + return resourceManagerRpc.replace(projectPb); + } + }, options().retryParams(), EXCEPTION_HANDLER)); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + @Override + public void undelete(final String projectId) { + try { + runWithRetries(new Callable() { + @Override + public Void call() { + resourceManagerRpc.undelete(projectId); + return null; + } + }, options().retryParams(), EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw ResourceManagerException.translateAndThrow(e); + } + } + + private Map optionMap(Option... options) { + Map temp = Maps.newEnumMap(ResourceManagerRpc.Option.class); + for (Option option : options) { + Object prev = temp.put(option.rpcOption(), option.value()); + checkArgument(prev == null, "Duplicate option %s", option); + } + return ImmutableMap.copyOf(temp); + } +} diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java index 990163c459da..563d1f9c3489 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableSet; import com.google.gcloud.ServiceOptions; +import com.google.gcloud.spi.DefaultResourceManagerRpc; import com.google.gcloud.spi.ResourceManagerRpc; import com.google.gcloud.spi.ResourceManagerRpcFactory; @@ -28,15 +29,17 @@ public class ResourceManagerOptions private static final long serialVersionUID = 538303101192527452L; private static final String GCRM_SCOPE = "https://www.googleapis.com/auth/cloud-platform"; - private static final Set SCOPES = ImmutableSet.of(GCRM_SCOPE); + private static final String READ_ONLY_GCRM_SCOPE = + "https://www.googleapis.com/auth/cloud-platform.read-only"; + private static final Set SCOPES = ImmutableSet.of(GCRM_SCOPE, READ_ONLY_GCRM_SCOPE); + private static final String DEFAULT_HOST = "https://cloudresourcemanager.googleapis.com"; public static class DefaultResourceManagerFactory implements ResourceManagerFactory { private static final ResourceManagerFactory INSTANCE = new DefaultResourceManagerFactory(); @Override public ResourceManager create(ResourceManagerOptions options) { - // return new ResourceManagerImpl(options); - return null; // TODO(ajaykannan): Fix me! + return new ResourceManagerImpl(options); } } @@ -53,11 +56,15 @@ public static class DefaultResourceManagerRpcFactory implements ResourceManagerR @Override public ResourceManagerRpc create(ResourceManagerOptions options) { - // return new DefaultResourceManagerRpc(options); - return null; // TODO(ajaykannan): Fix me! + return new DefaultResourceManagerRpc(options); } } + @Override + protected String defaultHost() { + return DEFAULT_HOST; + } + public static class Builder extends ServiceOptions.Builder { diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java index beb824ac812d..61ff82519afe 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java @@ -236,7 +236,10 @@ private static Map parseListOptions(String query) { String[] argEntry = arg.split("="); switch (argEntry[0]) { case "fields": - options.put("fields", argEntry[1].split(",")); + // List fields are in the form "projects(field1, field2, ...)" + options.put( + "fields", + argEntry[1].substring("projects(".length(), argEntry[1].length() - 1).split(",")); break; case "filter": options.put("filter", argEntry[1].split(" ")); diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java index 785aa88d49fb..0b8438b755d6 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java @@ -19,7 +19,6 @@ import org.junit.Test; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; public class LocalResourceManagerHelperTest { @@ -295,16 +294,21 @@ public void testList() { COMPLETE_PROJECT.getProjectId(), "DELETE_REQUESTED"); rpc.create(PROJECT_WITH_PARENT); projects = rpc.list(EMPTY_RPC_OPTIONS); - Iterator it = - projects.y().iterator(); - compareReadWriteFields(COMPLETE_PROJECT, it.next()); - compareReadWriteFields(PROJECT_WITH_PARENT, it.next()); + for (com.google.api.services.cloudresourcemanager.model.Project p : projects.y()) { + if (p.getProjectId().equals(COMPLETE_PROJECT.getProjectId())) { + compareReadWriteFields(COMPLETE_PROJECT, p); + } else if (p.getProjectId().equals(PROJECT_WITH_PARENT.getProjectId())) { + compareReadWriteFields(PROJECT_WITH_PARENT, p); + } else { + fail("Unexpected project in list."); + } + } } @Test public void testListFieldOptions() { Map rpcOptions = new HashMap<>(); - rpcOptions.put(ResourceManagerRpc.Option.FIELDS, "projectId,name,labels"); + rpcOptions.put(ResourceManagerRpc.Option.FIELDS, "projects(projectId,name,labels)"); rpcOptions.put(ResourceManagerRpc.Option.PAGE_TOKEN, "somePageToken"); rpcOptions.put(ResourceManagerRpc.Option.PAGE_SIZE, 1); rpc.create(PROJECT_WITH_PARENT); diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java new file mode 100644 index 000000000000..746159138bd0 --- /dev/null +++ b/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java @@ -0,0 +1,322 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.resourcemanager; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableMap; +import com.google.gcloud.Page; +import com.google.gcloud.RetryParams; +import com.google.gcloud.resourcemanager.ProjectInfo.ResourceId; +import com.google.gcloud.resourcemanager.ResourceManager.ProjectField; +import com.google.gcloud.resourcemanager.ResourceManager.ProjectGetOption; +import com.google.gcloud.resourcemanager.ResourceManager.ProjectListOption; +import com.google.gcloud.resourcemanager.testing.LocalResourceManagerHelper; +import com.google.gcloud.spi.ResourceManagerRpc; +import com.google.gcloud.spi.ResourceManagerRpcFactory; + +import org.easymock.EasyMock; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Map; + +public class ResourceManagerImplTest { + + private static final LocalResourceManagerHelper RESOURCE_MANAGER_HELPER = + LocalResourceManagerHelper.create(); + private static final ResourceManager RESOURCE_MANAGER = ResourceManagerOptions.builder() + .host("http://localhost:" + RESOURCE_MANAGER_HELPER.port()) + .build() + .service(); + private static final ProjectGetOption GET_FIELDS = + ProjectGetOption.fields(ProjectField.NAME, ProjectField.CREATE_TIME); + private static final ProjectListOption LIST_FIELDS = + ProjectListOption.fields(ProjectField.NAME, ProjectField.LABELS); + private static final ProjectListOption LIST_FILTER = + ProjectListOption.filter("id:* name:myProject labels.color:blue LABELS.SIZE:*"); + private static final ProjectInfo PARTIAL_PROJECT = ProjectInfo.builder("partial-project").build(); + private static final ResourceId PARENT = new ResourceId("id", "type"); + private static final ProjectInfo COMPLETE_PROJECT = ProjectInfo.builder("complete-project") + .name("name") + .labels(ImmutableMap.of("k1", "v1")) + .parent(PARENT) + .build(); + private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void beforeClass() { + RESOURCE_MANAGER_HELPER.start(); + } + + @Before + public void setUp() { + clearProjects(); + } + + private void clearProjects() { + for (ProjectInfo project : RESOURCE_MANAGER.list().values()) { + RESOURCE_MANAGER_HELPER.removeProject(project.projectId()); + } + } + + @AfterClass + public static void afterClass() { + RESOURCE_MANAGER_HELPER.stop(); + } + + private void compareReadWriteFields(ProjectInfo expected, ProjectInfo actual) { + assertEquals(expected.projectId(), actual.projectId()); + assertEquals(expected.name(), actual.name()); + assertEquals(expected.labels(), actual.labels()); + assertEquals(expected.parent(), actual.parent()); + } + + @Test + public void testCreate() { + ProjectInfo returnedProject = RESOURCE_MANAGER.create(PARTIAL_PROJECT); + compareReadWriteFields(PARTIAL_PROJECT, returnedProject); + assertEquals(ProjectInfo.State.ACTIVE, returnedProject.state()); + assertTrue(returnedProject.labels().isEmpty()); + assertNull(returnedProject.name()); + assertNull(returnedProject.parent()); + assertNotNull(returnedProject.projectNumber()); + assertNotNull(returnedProject.createTimeMillis()); + try { + RESOURCE_MANAGER.create(PARTIAL_PROJECT); + fail("Should fail, project already exists."); + } catch (ResourceManagerException e) { + assertEquals(409, e.code()); + assertTrue(e.getMessage().startsWith("A project with the same project ID") + && e.getMessage().endsWith("already exists.")); + } + returnedProject = RESOURCE_MANAGER.create(COMPLETE_PROJECT); + compareReadWriteFields(COMPLETE_PROJECT, returnedProject); + assertEquals(ProjectInfo.State.ACTIVE, returnedProject.state()); + assertNotNull(returnedProject.projectNumber()); + assertNotNull(returnedProject.createTimeMillis()); + } + + @Test + public void testDelete() { + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + RESOURCE_MANAGER.delete(COMPLETE_PROJECT.projectId()); + assertEquals( + ProjectInfo.State.DELETE_REQUESTED, + RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId()).state()); + try { + RESOURCE_MANAGER.delete("some-nonexistant-project-id"); + fail("Should fail because the project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().contains("not found.")); + } + } + + @Test + public void testGet() { + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + ProjectInfo returnedProject = RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId()); + compareReadWriteFields(COMPLETE_PROJECT, returnedProject); + RESOURCE_MANAGER_HELPER.removeProject(COMPLETE_PROJECT.projectId()); + try { + RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId()); + fail("Should fail because the project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().contains("not found")); + } + } + + @Test + public void testGetWithOptions() { + ProjectInfo originalProject = RESOURCE_MANAGER.create(COMPLETE_PROJECT); + ProjectInfo returnedProject = RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId(), GET_FIELDS); + assertFalse(COMPLETE_PROJECT.equals(returnedProject)); + assertEquals(COMPLETE_PROJECT.projectId(), returnedProject.projectId()); + assertEquals(COMPLETE_PROJECT.name(), returnedProject.name()); + assertEquals(originalProject.createTimeMillis(), returnedProject.createTimeMillis()); + assertNull(returnedProject.parent()); + assertNull(returnedProject.projectNumber()); + assertNull(returnedProject.state()); + assertTrue(returnedProject.labels().isEmpty()); + } + + @Test + public void testList() { + Page projects = RESOURCE_MANAGER.list(); + assertFalse(projects.values().iterator().hasNext()); // change this when #421 is resolved + RESOURCE_MANAGER.create(PARTIAL_PROJECT); + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + for (ProjectInfo p : RESOURCE_MANAGER.list().values()) { + if (p.projectId().equals(PARTIAL_PROJECT.projectId())) { + compareReadWriteFields(PARTIAL_PROJECT, p); + } else if (p.projectId().equals(COMPLETE_PROJECT.projectId())) { + compareReadWriteFields(COMPLETE_PROJECT, p); + } else { + fail("Some unexpected project returned by list."); + } + } + } + + @Test + public void testListFieldOptions() { + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + Page projects = RESOURCE_MANAGER.list(LIST_FIELDS); + ProjectInfo returnedProject = projects.iterateAll().next(); + assertEquals(COMPLETE_PROJECT.projectId(), returnedProject.projectId()); + assertEquals(COMPLETE_PROJECT.name(), returnedProject.name()); + assertEquals(COMPLETE_PROJECT.labels(), returnedProject.labels()); + assertNull(returnedProject.parent()); + assertNull(returnedProject.projectNumber()); + assertNull(returnedProject.state()); + assertNull(returnedProject.createTimeMillis()); + } + + @Test + public void testListFilterOptions() { + ProjectInfo matchingProject = ProjectInfo.builder("matching-project") + .name("MyProject") + .labels(ImmutableMap.of("color", "blue", "size", "big")) + .build(); + ProjectInfo nonMatchingProject1 = ProjectInfo.builder("non-matching-project1") + .name("myProject") + .labels(ImmutableMap.of("color", "blue")) + .build(); + ProjectInfo nonMatchingProject2 = ProjectInfo.builder("non-matching-project2") + .name("myProj") + .labels(ImmutableMap.of("color", "blue", "size", "big")) + .build(); + ProjectInfo nonMatchingProject3 = ProjectInfo.builder("non-matching-project3").build(); + RESOURCE_MANAGER.create(matchingProject); + RESOURCE_MANAGER.create(nonMatchingProject1); + RESOURCE_MANAGER.create(nonMatchingProject2); + RESOURCE_MANAGER.create(nonMatchingProject3); + for (ProjectInfo p : RESOURCE_MANAGER.list(LIST_FILTER).values()) { + assertFalse(p.equals(nonMatchingProject1)); + assertFalse(p.equals(nonMatchingProject2)); + compareReadWriteFields(matchingProject, p); + } + } + + @Test + public void testReplace() { + ProjectInfo createdProject = RESOURCE_MANAGER.create(COMPLETE_PROJECT); + String newName = "new name"; + Map newLabels = ImmutableMap.of("new k1", "new v1"); + ProjectInfo anotherCompleteProject = ProjectInfo.builder(COMPLETE_PROJECT.projectId()) + .name(newName) + .labels(newLabels) + .projectNumber(987654321L) + .createTimeMillis(230682061315L) + .state(ProjectInfo.State.DELETE_REQUESTED) + .parent(createdProject.parent()) + .build(); + ProjectInfo returnedProject = RESOURCE_MANAGER.replace(anotherCompleteProject); + compareReadWriteFields(anotherCompleteProject, returnedProject); + assertEquals(createdProject.projectNumber(), returnedProject.projectNumber()); + assertEquals(createdProject.createTimeMillis(), returnedProject.createTimeMillis()); + assertEquals(createdProject.state(), returnedProject.state()); + ProjectInfo nonexistantProject = + ProjectInfo.builder("some-project-id-that-does-not-exist").build(); + try { + RESOURCE_MANAGER.replace(nonexistantProject); + fail("Should fail because the project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().contains("the project was not found")); + } + } + + @Test + public void testUndelete() { + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + RESOURCE_MANAGER.delete(COMPLETE_PROJECT.projectId()); + assertEquals( + ProjectInfo.State.DELETE_REQUESTED, + RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId()).state()); + RESOURCE_MANAGER.undelete(COMPLETE_PROJECT.projectId()); + ProjectInfo revivedProject = RESOURCE_MANAGER.get(COMPLETE_PROJECT.projectId()); + compareReadWriteFields(COMPLETE_PROJECT, revivedProject); + assertEquals(ProjectInfo.State.ACTIVE, revivedProject.state()); + try { + RESOURCE_MANAGER.undelete("invalid-project-id"); + fail("Should fail because the project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().contains("the project was not found")); + } + } + + @Test + public void testRetryableException() { + ResourceManagerRpcFactory rpcFactoryMock = EasyMock.createMock(ResourceManagerRpcFactory.class); + ResourceManagerRpc resourceManagerRpcMock = EasyMock.createMock(ResourceManagerRpc.class); + EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(ResourceManagerOptions.class))) + .andReturn(resourceManagerRpcMock); + EasyMock.replay(rpcFactoryMock); + ResourceManager resourceManagerMock = ResourceManagerOptions.builder() + .serviceRpcFactory(rpcFactoryMock) + .retryParams(RetryParams.defaultInstance()) + .build() + .service(); + EasyMock.expect(resourceManagerRpcMock.get(PARTIAL_PROJECT.projectId(), EMPTY_RPC_OPTIONS)) + .andThrow(new ResourceManagerException(500, "Internal Error", true)) + .andReturn(PARTIAL_PROJECT.toPb()); + EasyMock.replay(resourceManagerRpcMock); + ProjectInfo returnedProject = resourceManagerMock.get(PARTIAL_PROJECT.projectId()); + assertEquals(PARTIAL_PROJECT, returnedProject); + } + + @Test + public void testNonRetryableException() { + String projectId = "some-project-id"; + thrown.expect(ResourceManagerException.class); + thrown.expectMessage("Project " + projectId + " not found."); + RESOURCE_MANAGER.get(projectId); + } + + @Test + public void testRuntimeException() { + ResourceManagerRpcFactory rpcFactoryMock = EasyMock.createMock(ResourceManagerRpcFactory.class); + ResourceManagerRpc resourceManagerRpcMock = EasyMock.createMock(ResourceManagerRpc.class); + EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(ResourceManagerOptions.class))) + .andReturn(resourceManagerRpcMock); + EasyMock.replay(rpcFactoryMock); + ResourceManager resourceManagerMock = + ResourceManagerOptions.builder().serviceRpcFactory(rpcFactoryMock).build().service(); + String exceptionMessage = "Artificial runtime exception"; + EasyMock.expect(resourceManagerRpcMock.get(PARTIAL_PROJECT.projectId(), EMPTY_RPC_OPTIONS)) + .andThrow(new RuntimeException(exceptionMessage)); + EasyMock.replay(resourceManagerRpcMock); + thrown.expect(ResourceManagerException.class); + thrown.expectMessage(exceptionMessage); + resourceManagerMock.get(PARTIAL_PROJECT.projectId()); + } +} From 55f4fbceb107c05c2d558cd1cc02c407cf329214 Mon Sep 17 00:00:00 2001 From: Ajay Kannan Date: Wed, 16 Dec 2015 09:44:58 -0800 Subject: [PATCH 2/3] Fix docs and return null if project not found by get. --- README.md | 7 ++-- TESTING.md | 4 +-- gcloud-java-resourcemanager/README.md | 28 ++++++--------- .../resourcemanager/ResourceManager.java | 30 ++++++++-------- .../resourcemanager/ResourceManagerImpl.java | 36 ++++++++++--------- .../ResourceManagerOptions.java | 4 +-- .../gcloud/spi/DefaultResourceManagerRpc.java | 7 +++- .../LocalResourceManagerHelperTest.java | 16 ++------- .../ResourceManagerImplTest.java | 35 ++++++++++-------- 9 files changed, 80 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index f8c04e2ffc57..abb241c3aa1b 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,6 @@ Google Cloud Resource Manager Here is a code snippet showing a simple usage example. Note that you must supply Google SDK credentials for this service, not other forms of authentication listed in the [Authentication section](#authentication). ```java -import com.google.common.collect.ImmutableMap; import com.google.gcloud.resourcemanager.ProjectInfo; import com.google.gcloud.resourcemanager.ResourceManager; import com.google.gcloud.resourcemanager.ResourceManagerOptions; @@ -203,10 +202,10 @@ import java.util.Iterator; ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service(); ProjectInfo myProject = resourceManager.get("some-project-id-that-I-own"); -ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() - .labels(ImmutableMap.of("launch-status", "in-development")).build()); +ProjectInfo newProjectInfo = resourceManager.replace(myProject.toBuilder() + .addLabel("launch-status", "in-development").build()); System.out.println("Updated the labels of project " + newProjectInfo.projectId() - + " to be " + newProjectInfo.labels() + System.lineSeparator()); + + " to be " + newProjectInfo.labels()); // List all the projects you have permission to view. Iterator projectIterator = resourceManager.list().iterateAll(); System.out.println("Projects I can view:"); diff --git a/TESTING.md b/TESTING.md index d6edd000e6d6..7f513a023621 100644 --- a/TESTING.md +++ b/TESTING.md @@ -82,7 +82,7 @@ You can test against a temporary local Resource Manager by following these steps This will spawn a server thread that listens to `localhost` at an ephemeral port for Resource Manager requests. -2. In your program, create and use a Resource Manager service object whose host is set host to `localhost` at the appropriate port. For example: +2. In your program, create and use a Resource Manager service object whose host is set to `localhost` at the appropriate port. For example: ```java ResourceManager resourceManager = ResourceManagerOptions.builder() @@ -97,7 +97,7 @@ You can test against a temporary local Resource Manager by following these steps helper.stop(); ``` - This method will block a short amount of time until the server thread has been terminated. + This method will block until the server thread has been terminated. #### On a remote machine diff --git a/gcloud-java-resourcemanager/README.md b/gcloud-java-resourcemanager/README.md index 2a229720536c..a58386776345 100644 --- a/gcloud-java-resourcemanager/README.md +++ b/gcloud-java-resourcemanager/README.md @@ -50,7 +50,7 @@ with the Cloud Resource Manager using this client Library. Getting Started --------------- #### Prerequisites -You will also need to set up the local development environment by [installing the Google Cloud SDK](https://cloud.google.com/sdk/) and running the following commands in command line: `gcloud auth login`. +You will need to set up the local development environment by [installing the Google Cloud SDK](https://cloud.google.com/sdk/) and running the following command in command line: `gcloud auth login`. > Note: You don't need a project ID to use this service. If you have a project ID set in the Google Cloud SDK, you can unset it by typing `gcloud config unset project` in command line. @@ -68,7 +68,7 @@ ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().servi ``` #### Creating a project -All you need to create a project is a globally unique project ID. You can also optionally attach a non-unique name and labels to your project. Read more about naming guidelines for project IDs, names, and labels [here](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects). To create a project, add the following imports at the top of your file: +All you need to create a project is a globally unique project ID. You can also optionally attach a non-unique name and labels to your project. Read more about naming guidelines for project IDs, names, and labels [here](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects). To create a project, add the following import at the top of your file: ```java import com.google.gcloud.resourcemanager.ProjectInfo; @@ -84,7 +84,7 @@ ProjectInfo myProject = resourceManager.create(ProjectInfo.builder(myProjectId). Note that the return value from `create` is a `ProjectInfo` that includes additional read-only information, like creation time, project number, and lifecycle state. Read more about these fields on the [Projects page](https://cloud.google.com/resource-manager/reference/rest/v1beta1/projects). #### Getting a specific project -You can load a project if you know it's project ID and have read permissions to the project. For example, say we wanted to get the project we just created. We can do the following: +You can load a project if you know it's project ID and have read permissions to the project. For example, to get the project we just created we can do the following: ```java ProjectInfo projectFromServer = resourceManager.get(myProjectId); @@ -93,23 +93,17 @@ ProjectInfo projectFromServer = resourceManager.get(myProjectId); #### Editing a project To edit a project, create a new `ProjectInfo` object and pass it in to the `ResourceManager.replace` method. -Suppose that you want to add a label for the newly created project to denote that it's launch status is "in development". Import the following: - -```java -import com.google.common.collect.ImmutableMap; -``` - -Then add the following code to your program: +For example, to add a label for the newly created project to denote that it's launch status is "in development", add the following code: ```java ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() - .labels(ImmutableMap.of("launch-status", "in-development")).build()); + .addLabel("launch-status", "in-development").build()); ``` Note that the values of the project you pass in to `replace` overwrite the server's values for non-read-only fields, namely `projectName` and `labels`. For example, if you create a project with `projectName` "some-project-name" and subsequently call replace using a `ProjectInfo` object that didn't set the `projectName`, then the server will unset the project's name. The server ignores any attempted changes to the read-only fields `projectNumber`, `lifecycleState`, and `createTime`. The `projectId` cannot change. #### Listing all projects -Suppose that we want list of all projects for which we have read permissions. Add the following import: +Suppose that we want a list of all projects for which we have read permissions. Add the following import: ```java import java.util.Iterator; @@ -130,7 +124,6 @@ while (projectIterator.hasNext()) { Here we put together all the code shown above into one program. This program assumes that you are running from your own desktop. ```java -import com.google.common.collect.ImmutableMap; import com.google.gcloud.resourcemanager.ProjectInfo; import com.google.gcloud.resourcemanager.ResourceManager; import com.google.gcloud.resourcemanager.ResourceManagerOptions; @@ -150,14 +143,13 @@ public class GcloudJavaResourceManagerExample { // Get a project from the server. ProjectInfo projectFromServer = resourceManager.get(myProjectId); - System.out.println("Got project " + projectFromServer.projectId() + " from the server." - + System.lineSeparator()); + System.out.println("Got project " + projectFromServer.projectId() + " from the server."); // Update a project - ProjectInfo newProjectInfo = resourceManager.replace(projectFromServer.toBuilder() - .labels(ImmutableMap.of("launch-status", "in-development")).build()); + ProjectInfo newProjectInfo = resourceManager.replace(myProject.toBuilder() + .addLabel("launch-status", "in-development").build()); System.out.println("Updated the labels of project " + newProjectInfo.projectId() - + " to be " + newProjectInfo.labels() + System.lineSeparator()); + + " to be " + newProjectInfo.labels()); // List all the projects you have permission to view. Iterator projectIterator = resourceManager.list().iterateAll(); diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java index 1562fe51dad1..9ae524e28017 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java @@ -36,7 +36,7 @@ public interface ResourceManager extends Service { /** * The fields of a project. * - * These values can be used to specify the fields to include in a partial response when calling + *

These values can be used to specify the fields to include in a partial response when calling * {@link ResourceManager#get} or {@link ResourceManager#list}. Project ID is always returned, * even if not specified. */ @@ -82,7 +82,7 @@ private ProjectGetOption(ResourceManagerRpc.Option option, Object value) { /** * Returns an option to specify the project's fields to be returned by the RPC call. * - * If this option is not provided all project fields are returned. + *

If this option is not provided all project fields are returned. * {@code ProjectGetOption.fields} can be used to specify only the fields of interest. Project * ID is always returned, even if not specified. {@link ProjectField} provides a list of fields * that can be used. @@ -106,17 +106,17 @@ private ProjectListOption(ResourceManagerRpc.Option option, Object value) { /** * Returns an option to specify a filter. * - * Filter rules are case insensitive. The fields eligible for filtering are: + *

Filter rules are case insensitive. The fields eligible for filtering are: *

    *
  • name *
  • project ID *
  • labels.key, where key is the name of a label *
* - * You can specify multiple filters by adding a space between each filter. Multiple filters + *

You can specify multiple filters by adding a space between each filter. Multiple filters * are composed using "and". * - * Some examples of filters: + *

Some examples of filters: *