diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d93f2d032c7..3ed827deb5af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,9 @@ Contributing ============ 1. **Please sign one of the contributor license agreements below.** -1. Fork the repo, develop and test your code changes, add docs. -1. Make sure that your commit messages clearly describe the changes. -1. Send a pull request. +2. Fork the repo, develop and test your code changes, add docs. +3. Make sure that your commit messages clearly describe the changes. +4. Send a pull request. Here are some guidelines for hacking on gcloud-java. @@ -43,6 +43,9 @@ The feature must work fully on Java 7 and above. The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should be discussed). +Adding Support for a New Service +-------------------------------- +See [SUPPORTING_NEW_SERVICES](./SUPPORTING_NEW_SERVICES.md) for guidelines on how to add support for a new Google Cloud service to `gcloud-java`. Coding Style ------------ diff --git a/README.md b/README.md index 68c624c37489..6bc56bad250a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Java idiomatic client for [Google Cloud Platform][cloud-platform] services. [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) @@ -16,6 +16,7 @@ This client supports the following Google Cloud Platform services: - [Google Cloud BigQuery] (#google-cloud-bigquery-alpha) (Alpha) - [Google Cloud Datastore] (#google-cloud-datastore) +- [Google Cloud DNS] (#google-cloud-dns-alpha) (Alpha) - [Google Cloud Resource Manager] (#google-cloud-resource-manager-alpha) (Alpha) - [Google Cloud Storage] (#google-cloud-storage) @@ -24,38 +25,48 @@ This client supports the following Google Cloud Platform services: Quickstart ---------- + +> As of April 12th, 2016, `gcloud-java`'s group ID and base package were renamed to `com.google.cloud`. If you haven't already, please update your dependencies. + If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java:0.1.5' +compile 'com.google.cloud:gcloud-java:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java" % "0.2.0" ``` Example Applications -------------------- -- [`BigQueryExample`](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java) - A simple command line interface providing some of Cloud BigQuery's functionality - - Read more about using this application on the [`BigQueryExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/bigquery/BigQueryExample.html). +- [`BigQueryExample`](./gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java) - A simple command line interface providing some of Cloud BigQuery's functionality + - Read more about using this application on the [`BigQueryExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/bigquery/BigQueryExample.html). - [`Bookshelf`](https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/bookshelf) - An App Engine app that manages a virtual bookshelf. - This app uses `gcloud-java` to interface with Cloud Datastore and Cloud Storage. It also uses Cloud SQL, another Google Cloud Platform service. -- [`DatastoreExample`](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/DatastoreExample.java) - A simple command line interface for the Cloud Datastore - - Read more about using this application on the [`DatastoreExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/datastore/DatastoreExample.html). -- [`ResourceManagerExample`](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/ResourceManagerExample.java) - A simple command line interface providing some of Cloud Resource Manager's functionality - - Read more about using this application on the [`ResourceManagerExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/resourcemanager/ResourceManagerExample.html). -- [`SparkDemo`](https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/managed_vms/sparkjava) - An example of using gcloud-java-datastore from within the SparkJava and App Engine Managed VM frameworks. +- [`DatastoreExample`](./gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java) - A simple command line interface for Cloud Datastore + - Read more about using this application on the [`DatastoreExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/datastore/DatastoreExample.html). +- [`DnsExample`](./gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java) - A simple command line interface for Cloud DNS + - Read more about using this application on the [`DnsExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/dns/DnsExample.html). +- [`Flexible Environment/Datastore example`](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/managed_vms/datastore) - A simple app that uses Cloud Datastore to list the last 10 IP addresses that visited your site. + - Read about how to run the application [here](https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/managed_vms/README.md). +- [`Flexible Environment/Storage example`](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/managed_vms/cloudstorage) - An app that uploads files to a public Cloud Storage bucket on the App Engine Flexible Environment runtime. +- [`ResourceManagerExample`](./gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java) - A simple command line interface providing some of Cloud Resource Manager's functionality + - Read more about using this application on the [`ResourceManagerExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/resourcemanager/ResourceManagerExample.html). +- [`SparkDemo`](https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/managed_vms/sparkjava) - An example of using `gcloud-java-datastore` from within the SparkJava and App Engine Flexible Environment frameworks. - Read about how it works on the example's [README page](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/managed_vms/sparkjava#how-does-it-work). -- [`StorageExample`](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/StorageExample.java) - A simple command line interface providing some of Cloud Storage's functionality - - Read more about using this application on the [`StorageExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/storage/StorageExample.html). +- [`StorageExample`](./gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java) - A simple command line interface providing some of Cloud Storage's functionality + - Read more about using this application on the [`StorageExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/storage/StorageExample.html). +- [`TaskList`](https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/datastore/src/main/java/com/google/datastore/snippets/TaskList.java) - A command line application that uses Cloud Datastore to manage a to-do list. + - Read about how to run the application on its [README page](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/datastore). Specifying a Project ID ----------------------- @@ -81,8 +92,9 @@ Most `gcloud-java` libraries require a project ID. There are multiple ways to s 1. Project ID supplied when building the service options 2. Project ID specified by the environment variable `GCLOUD_PROJECT` 3. App Engine project ID -4. Google Cloud SDK project ID -5. Compute Engine project ID +4. Project ID specified in the JSON credentials file pointed by the `GOOGLE_APPLICATION_CREDENTIALS` environment variable +5. Google Cloud SDK project ID +6. Compute Engine project ID Authentication -------------- @@ -126,19 +138,19 @@ Google Cloud BigQuery (Alpha) Here is a code snippet showing a simple usage example from within Compute/App Engine. Note that you must [supply credentials](#authentication) and a project ID if running this snippet elsewhere. Complete source code can be found at -[CreateTableAndLoadData.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/CreateTableAndLoadData.java). +[CreateTableAndLoadData.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java). ```java -import com.google.gcloud.bigquery.BigQuery; -import com.google.gcloud.bigquery.BigQueryOptions; -import com.google.gcloud.bigquery.Field; -import com.google.gcloud.bigquery.FormatOptions; -import com.google.gcloud.bigquery.Job; -import com.google.gcloud.bigquery.Schema; -import com.google.gcloud.bigquery.StandardTableDefinition; -import com.google.gcloud.bigquery.Table; -import com.google.gcloud.bigquery.TableId; -import com.google.gcloud.bigquery.TableInfo; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.FormatOptions; +import com.google.cloud.bigquery.Job; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableId; +import com.google.cloud.bigquery.TableInfo; BigQuery bigquery = BigQueryOptions.defaultInstance().service(); TableId tableId = TableId.of("dataset", "table"); @@ -174,15 +186,15 @@ Google Cloud Datastore Here are two code snippets showing simple usage examples from within Compute/App Engine. Note that you must [supply credentials](#authentication) and a project ID if running this snippet elsewhere. The first snippet shows how to create a Datastore entity. Complete source code can be found at -[CreateEntity.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/CreateEntity.java). +[CreateEntity.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/CreateEntity.java). ```java -import com.google.gcloud.datastore.Datastore; -import com.google.gcloud.datastore.DatastoreOptions; -import com.google.gcloud.datastore.DateTime; -import com.google.gcloud.datastore.Entity; -import com.google.gcloud.datastore.Key; -import com.google.gcloud.datastore.KeyFactory; +import com.google.cloud.datastore.Datastore; +import com.google.cloud.datastore.DatastoreOptions; +import com.google.cloud.datastore.DateTime; +import com.google.cloud.datastore.Entity; +import com.google.cloud.datastore.Key; +import com.google.cloud.datastore.KeyFactory; Datastore datastore = DatastoreOptions.defaultInstance().service(); KeyFactory keyFactory = datastore.newKeyFactory().kind("keyKind"); @@ -196,14 +208,14 @@ datastore.put(entity); ``` The second snippet shows how to update a Datastore entity if it exists. Complete source code can be found at -[UpdateEntity.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/UpdateEntity.java). +[UpdateEntity.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/UpdateEntity.java). ```java -import com.google.gcloud.datastore.Datastore; -import com.google.gcloud.datastore.DatastoreOptions; -import com.google.gcloud.datastore.DateTime; -import com.google.gcloud.datastore.Entity; -import com.google.gcloud.datastore.Key; -import com.google.gcloud.datastore.KeyFactory; +import com.google.cloud.datastore.Datastore; +import com.google.cloud.datastore.DatastoreOptions; +import com.google.cloud.datastore.DateTime; +import com.google.cloud.datastore.Entity; +import com.google.cloud.datastore.Key; +import com.google.cloud.datastore.KeyFactory; Datastore datastore = DatastoreOptions.defaultInstance().service(); KeyFactory keyFactory = datastore.newKeyFactory().kind("keyKind"); @@ -218,6 +230,71 @@ if (entity != null) { } ``` +Google Cloud DNS (Alpha) +---------------------- +- [API Documentation][dns-api] +- [Official Documentation][cloud-dns-docs] + +*Follow the [activation instructions][cloud-dns-activation] to use the Google Cloud DNS API with your project.* + +#### Preview + +Here are two code snippets showing simple usage examples from within Compute/App Engine. Note that you must [supply credentials](#authentication) and a project ID if running this snippet elsewhere. + +The first snippet shows how to create a zone resource. Complete source code can be found on +[CreateZone.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateZone.java). + +```java +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.Zone; +import com.google.cloud.dns.ZoneInfo; + +Dns dns = DnsOptions.defaultInstance().service(); +String zoneName = "my-unique-zone"; +String domainName = "someexampledomain.com."; +String description = "This is a gcloud-java-dns sample zone."; +ZoneInfo zoneInfo = ZoneInfo.of(zoneName, domainName, description); +Zone zone = dns.create(zoneInfo); +``` + +The second snippet shows how to create records inside a zone. The complete code can be found on [CreateOrUpdateRecordSets.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateOrUpdateRecordSets.java). + +```java +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.RecordSet; +import com.google.cloud.dns.Zone; + +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +Dns dns = DnsOptions.defaultInstance().service(); +String zoneName = "my-unique-zone"; +Zone zone = dns.getZone(zoneName); +String ip = "12.13.14.15"; +RecordSet toCreate = RecordSet.builder("www.someexampledomain.com.", RecordSet.Type.A) + .ttl(24, TimeUnit.HOURS) + .addRecord(ip) + .build(); +ChangeRequestInfo.Builder changeBuilder = ChangeRequestInfo.builder().add(toCreate); + +// Verify that the record does not exist yet. +// If it does exist, we will overwrite it with our prepared record. +Iterator recordSetIterator = zone.listRecordSets().iterateAll(); +while (recordSetIterator.hasNext()) { + RecordSet current = recordSetIterator.next(); + if (toCreate.name().equals(current.name()) && + toCreate.type().equals(current.type())) { + changeBuilder.delete(current); + } +} + +ChangeRequestInfo changeRequest = changeBuilder.build(); +zone.applyChangeRequest(changeRequest); +``` + Google Cloud Resource Manager (Alpha) ---------------------- @@ -228,11 +305,11 @@ Google Cloud Resource Manager (Alpha) 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). Complete source code can be found at -[UpdateAndListProjects.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/UpdateAndListProjects.java). +[UpdateAndListProjects.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/UpdateAndListProjects.java). ```java -import com.google.gcloud.resourcemanager.Project; -import com.google.gcloud.resourcemanager.ResourceManager; -import com.google.gcloud.resourcemanager.ResourceManagerOptions; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; import java.util.Iterator; @@ -266,16 +343,16 @@ Google Cloud Storage Here are two code snippets showing simple usage examples from within Compute/App Engine. Note that you must [supply credentials](#authentication) and a project ID if running this snippet elsewhere. The first snippet shows how to create a Storage blob. Complete source code can be found at -[CreateBlob.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateBlob.java). +[CreateBlob.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateBlob.java). ```java import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.BlobInfo; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; Storage storage = StorageOptions.defaultInstance().service(); BlobId blobId = BlobId.of("bucket", "blob_name"); @@ -284,14 +361,14 @@ Blob blob = storage.create(blobInfo, "Hello, Cloud Storage!".getBytes(UTF_8)); ``` The second snippet shows how to update a Storage blob if it exists. Complete source code can be found at -[UpdateBlob.java](./gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/UpdateBlob.java). +[UpdateBlob.java](./gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/UpdateBlob.java). ```java import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; @@ -357,7 +434,11 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-datastore]: https://cloud.google.com/datastore/docs [cloud-datastore-docs]: https://cloud.google.com/datastore/docs [cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate -[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/datastore/package-summary.html +[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/datastore/package-summary.html + +[dns-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/dns/package-summary.html +[cloud-dns-docs]: https://cloud.google.com/dns/docs +[cloud-dns-activation]: https://console.cloud.google.com/start/api?id=dns [cloud-pubsub]: https://cloud.google.com/pubsub/ [cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs @@ -366,11 +447,11 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-storage-docs]: https://cloud.google.com/storage/docs/overview [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 +[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html -[resourcemanager-api]:http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/resourcemanager/package-summary.html +[resourcemanager-api]:http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/resourcemanager/package-summary.html [cloud-resourcemanager-docs]:https://cloud.google.com/resource-manager/ [cloud-bigquery]: https://cloud.google.com/bigquery/ [cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs/overview -[bigquery-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/bigquery/package-summary.html +[bigquery-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/bigquery/package-summary.html diff --git a/RELEASING.md b/RELEASING.md index 5e2d6202062e..97b6e6f716ca 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,10 +7,10 @@ Most of the release process is handled by the `after_success.sh` script, trigger 1. Run `utilities/update_pom_version.sh` from the repository's base directory. This script takes an optional argument denoting the new version. By default, if the current version is X.Y.Z-SNAPSHOT, the script will update the version in all the pom.xml files to X.Y.Z. If desired, another version can be supplied via command line argument instead. -2. Create a PR to update the pom.xml version. -The PR should look something like [#225](https://github.com/GoogleCloudPlatform/gcloud-java/pull/225). After this PR is merged into GoogleCloudPlatform/gcloud-java, Travis CI will push a new website to GoogleCloudPlatform/gh-pages, push a new artifact to the Maven Central Repository, and update versions in the README files. +2. Create a PR to update the pom.xml version. If releasing a new client library, this PR should also update javadoc grouping in the base directory's [pom.xml](./pom.xml). +PRs that don't release new modules should look something like [#225](https://github.com/GoogleCloudPlatform/gcloud-java/pull/225). PRs that do release a new module should also add the appropriate packages to the javadoc groups "SPI" and "Test helpers", as shown in [#802](https://github.com/GoogleCloudPlatform/gcloud-java/pull/802) for `gcloud-java-dns`. After this PR is merged into GoogleCloudPlatform/gcloud-java, Travis CI will push a new website to GoogleCloudPlatform/gh-pages, push a new artifact to the Maven Central Repository, and update versions in the README files. Do not merge in any non-release-related pull requests between the start of step 2 and the end of step 6. Between these steps, the project version is a non-snapshot version, so any commits to the master branch will cause Travis to spend extra resources attempting to redeploy artifacts. -3. Before moving on, verify that the artifacts have successfully been pushed to the Maven Central Repository. Open Travis CI, click the ["Build History" tab](https://travis-ci.org/GoogleCloudPlatform/gcloud-java/builds), and open the second build's logs for Step 2's PR. Be sure that you are not opening the "Pull Request" build logs. When the build finishes, scroll to the end of the log and verify that the artifacts were successfully staged and deployed. You can also search for `gcloud-java` on the [Sonatype website](https://oss.sonatype.org/#nexus-search;quick~gcloud-java) and check the latest version number. If the deployment didn't succeed because of a flaky test, rerun the build. +3. Before moving on, verify that the artifacts have successfully been pushed to the Maven Central Repository. Open Travis CI, click the ["Build History" tab](https://travis-ci.org/GoogleCloudPlatform/gcloud-java/builds), and open the second build's logs for Step 2's PR. Be sure that you are not opening the "Pull Request" build logs. When the build finishes, scroll to the end of the log and verify that the artifacts were successfully staged and deployed. Search for `gcloud-java` on the [Sonatype website](https://oss.sonatype.org/#nexus-search;quick~gcloud-java) and check the latest version number. In rare cases (when the Maven plugin that determines the version of the repository fails), the artifacts may not be deployed even if the version in the pom.xml files doesn't contain `SNAPSHOT`. If the artifacts weren't deployed due to invalid version parsing or a flaky test, rerun the build. 4. Publish a release on Github manually. Go to the [releases page](https://github.com/GoogleCloudPlatform/gcloud-java/releases) and open the appropriate release draft. Make sure the "Tag Version" is `vX.Y.Z` and the "Release Title" is `X.Y.Z`, where `X.Y.Z` is the release version as listed in the `pom.xml` files. The draft should already have all changes that impact users since the previous release. To double check this, you can use the `git log` command and look through the merged master branch pull requests. Here is an example of the log command to get non-merge commits between v0.0.12 and v0.1.0: @@ -26,7 +26,7 @@ As mentioned before, there is an optional version argument. By default, the scr 6. Create and merge in another PR to reflect the updated project version. For an example of what this PR should look like, see [#227](https://github.com/GoogleCloudPlatform/gcloud-java/pull/227). -7. Be sure to update App Engine documentation and [java-docs-samples](https://github.com/GoogleCloudPlatform/java-docs-samples) code as necessary. See directions [here](https://docs.google.com/a/google.com/document/d/1SS3xNn2v0qW7EadGUPBUAPIQAH5VY6WSFmT17ZjjUVE/edit?usp=sharing). +7. Be sure to update Google Cloud Platform docs, [java-docs-samples](https://github.com/GoogleCloudPlatform/java-docs-samples) code/docs, and [getting-started-java](https://github.com/GoogleCloudPlatform/getting-started-java) code/docs. See directions [here](https://docs.google.com/a/google.com/document/d/1SS3xNn2v0qW7EadGUPBUAPIQAH5VY6WSFmT17ZjjUVE/). ### To push a snapshot version diff --git a/SUPPORTING_NEW_SERVICES.md b/SUPPORTING_NEW_SERVICES.md new file mode 100644 index 000000000000..7eba222336de --- /dev/null +++ b/SUPPORTING_NEW_SERVICES.md @@ -0,0 +1,114 @@ +## Supporting New Services + +### Overview + +This document outlines how to add support for a new service in `gcloud-java`. New services should be submodules located in a folder within the main repository, built using Maven. A new service should contain the following items: + +* An API layer, with which users will interact. This includes model objects and a service class. +* An SPI layer, which translates gcloud-java API calls into RPCs using an autogenerated client library. In almost all use cases, the user will not directly interact with this code. Separating this code from the API layer provides two benefits. First, it allows the API layer to remain stable despite changes to the autogenerated libraries used. Second, it makes testing easier, since the RPC implementation can be substituted with a mock. +* A test helper class, which allows users to easily interact with a local service emulator (if possible). If there is no emulator available and the service is too complex to create a mock, then a remote test helper should be provided to separate test data from other user data and enable easy cleanup. +* Tests, including unit tests and integration tests. +* A command line example application. +* Documentation, which is comprised of READMEs, Javadoc, and code snippets. + +### Components of a new service + +#### API layer + +Before starting work on the API layer, write a design document and provide sample API code. Sample API code should either be included in the design document or as a pull request tagged with the "don't merge" label. As part of the design process, be sure to examine the Google Cloud service API and any implementations provided in other gcloud-* language libraries. Solicit feedback from other contributors to the repository. `gcloud-java` services should be low-level while minimizing boilerplate code needed by API users. They should also be flexible enough to be used by higher-level libraries. For example, [Objectify](https://github.com/objectify/objectify) should be able to use `gcloud-java-datastore`. + +The library should contain: +* A subclass of the [`ServiceOptions`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java) class. The `ServiceOptions` class contains important information for communicating with the Google Cloud service, such as the project ID, authentication, and retry handling. Subclasses should add/override relevant methods as necessary. Example: [`DatastoreOptions`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreOptions.java). +* An interface extending the [`Service`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/Service.java) interface (example: [`Datastore`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Datastore.java)) and an implentation of that interface (example: [`DatastoreImpl`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java)). +* An interface extending the [`ServiceFactory`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/ServiceFactory.java) interface. Example: [`DatastoreFactory`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreFactory.java) +* A runtime exception class that extends [`BaseServiceException`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java), which is used to wrap service-related exceptions. Example: [`DatastoreException`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreException.java) +* Classes representing model objects and request-related options. Model objects that correspond to service resources should have a subclass that provides functions related to that resource. For example, see [`BlobInfo`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobInfo.java) (the metadata class) and [`Blob`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java) (the functional class). The builders for both objects should implement a common interface or abstract class, and the functional subclass builder should delegate to the metadata class builder. +* Request-related options classes. Operations should accept these options as varargs when appropriate. Supplying options as varargs allows for supporting more advanced use cases without affecting the method signature. The options classes should provide static methods to create instances with specific options settings. A common option is to request that only specific fields of a model object should be included in a response. Typically such an option is created via a `fields(...)` method which accepts a vararg of type `Field` enum. The enum should implement the [FieldSelector](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java) interface. + +In general, make classes immutable whenever possible, providing builders as necessary. Make model object classes `java.util.Serializable`. Prefer making classes final, with the following exceptions: (1) functional objects and (2) classes in which the user cannot set all attributes. If a class cannot be made final, then `hashCode` or `equals` overrides should be made final if possible. + +`gcloud-java-core` provides functionality for code patterns used across `gcloud-java` libraries. The following are some important core concepts: +* Paging: Google Cloud services often expose page-based listing using page tokens. The [`Page`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/Page.java) interface should be used for page-based listing. A `Page` contains an iterator over results in that page, as well as methods to get the next page and all results in future pages. `Page` requires a `NextPageFetcher` implementation (see the `NextPageFetcher` interface in [`PageImpl`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/PageImpl.java)). This implementation should delegate constructing request options to the `nextRequestOptions` method in `PageImpl`. + +* Exception handling: we have the notion of retryable versus non-retryable operations. These are encapsulated by `BaseServiceException`. Retryable error codes should be listed in the service's subclass of `BaseServiceException`. An operation should be considered retryable if it makes sense to retry (e.g. if there was a transient service error, not a fundamentally invalid request) and if the operation that triggered the exception is idempotent. Exceptions also contain information regarding whether the service rejected the request, meaning the operation was not applied. The `BaseServiceException` subclass should also provide methods to translate the exceptions given by the underlying autogeneraged client library. The [`ExceptionHandler`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/ExceptionHandler.java) class intercepts and retries RPC calls when retryable exceptions are encountered. Note that some exceptions are masked in the SPI layer. For example, `get` and `delete` operations often return "404 Not Found" if the resource doesn't exist. Instead of throwing an exception in these cases, we often return `null` for `get` and `false` for `delete`. + +* Batching: The [`BatchResult`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/dns-alpha-batch/gcloud-java-core/src/main/java/com/google/cloud/BatchResult.java) class provides a simple way for users to combine RPC calls for performance enhancement. APIs for services that support batching should implement a batch class that contains methods similar to the methods in the `Service.java` subclass. A batch operation's return type should be a subclass of `BatchResult`. Also provide an SPI-layer class to collect batch requests and submit the batch. A batch instance should be created by the service API, preferably via a method called `batch()`. The batch should be submitted using the batch instance itself, preferably using a method named `submit()`. + +* IAM Policies: If the Google Cloud service supports [IAM](https://cloud.google.com/iam/docs/), you should provide a subclass of [`IamPolicy`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-core/src/main/java/com/google/cloud/IamPolicy.java) in your API. [`Policy`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Policy.java) can be used to set default access control lists on Google Cloud projects as a whole. However, if users want to set policies on specific resources within a project, they will need to use the subclass you provide in your API. + +Notes/reminders: +* API layer classes should be located in the package `com.google.cloud.servicename`, where "servicename" corresponds to the name of the Google Cloud service. +* Override the `ServiceOptions.defaultRetryParams()` method in your service's options class to align with the Service Level Agreement (SLA) given by the underlying service. See #857 and #860 for context. +* Override the `ServiceOptions.projectIdRequired()` method to return false when the service does not require requests to be associated with a specific project. +* See conventions about overriding the `equals` and `hashCode` methods in the discussion of #892. +* While not all fields for model objects need to be exposed to the user, `gcloud-java` services should get and set all relevant fields when making RPC calls to the Google Cloud service's API. For example, since the `parent` field of Cloud Resource Manager Project objects is in alpha (at the time this was written) and not available to most users, `gcloud-java-resourcemanager` gets and sets the parent when interacting with the Cloud Resource Manager, but does not expose the parent to users. As a result, the user won't accidentally unset the parent when updating a project. +* Be aware of differences in "update" behavior and name update/replace methods accordingly in your API. See #321 for context. +* Do not expose third party libraries in the API. This has been a design choice from the beginning of the project, and all existing `gcloud-java` services adhere to this convention. +* Member variable getters and builder setters should not use the JavaBean get/set prefix style. +* Any service-generated IDs for model objects should be named `generatedId()`. + +#### SPI layer + +The SPI layer classes should be located in the package `com.google.cloud.servicename.spi`. In most cases, the SPI layer should contain at least three classes: +* An RPC factory interface (allows for the implementation to be loaded via the `java.util.ServiceLoader`). +* An RPC interface that contains all RPC methods. +* A default RPC implementation. + +#### Test helpers + +Test helper classes should be located in the package `com.google.cloud.servicename.testing`. The naming convention for test helpers is `[Local|Remote][Service]Helper.java`. For example, the local test helper for `gcloud-java-datastore` is named `LocalDatastoreHelper` and the remote test helper for `gcloud-java-storage` is named `RemoteStorageHelper`. All test helpers should contain public `create` and `options` methods, and local helpers should contain `start` and `stop` methods. See existing test helpers for information on what each of those methods should do. + +There are three types of test helpers: +* When a local emulator is already available, your test helper should launch that emulator and return service options to connect to that local emulator. This enables both users and our own library to run unit tests easily. An example of this type of helper is `LocalDatastoreHelper`. Google Cloud Datastore provides a script that launches a local datastore, so `LocalDatastoreHelper` launches that script in a separate process when the user calls `start()`. + +* When there is no local emulator but the service is simple enough to write an emulator, you should do so. The emulator should listen to a port for requests, process those requests, and send responses back, being as true to the actual service as possible. Dependencies in this mock should be as lightweight as possible. Be sure to document differences between your emulator and the actual service. Examples of this type of test helper are `LocalResourceManagerHelper` and `LocalDnsHelper`. + +* When there is no local emulator and the service is too complex to write a solid emulator, the test helper should contain methods to get options and to help isolate test data from production data. `RemoteStorageHelper` is an example of this type of test helper, since there is no local emulator for Google Cloud Storage (at the time that this was written) and because the Google Cloud Storage API is complex. `RemoteStorageHelper` has methods to: + * Get service options settings. + * Create a test bucket with a sufficiently obscure name (to separate the bucket from any of the users other data). + * Clean up data left over from tests in that test bucket. + +#### Tests + +API-level functionality should be well-covered by unit tests. Coders and reviewers should examine test coverage to ensure that important code paths are not being left untested. As of now, `gcloud-java` relies on integration tests to test the SPI layer. Unit tests for the API layer should be located in the package `com.google.cloud.servicename`. Integration tests should be placed in a separate package, `com.google.cloud.servicename.it`, which enables us to catch method access bugs. Unit tests for the test helper should be placed in the package `com.google.cloud.servicename.testing`. All unit tests run for pull requests, but integration tests are only run upon merging the pull request. We only run integration tests upon merging pull requests to avoid decrypting and exposing credentials to anybody who can create a pull request from a fork. Prefix integration test file names with "IT" (e.g. `ITDnsTest`) to separate them from regular unit tests. + +Simple service-related tests should be added to [GoogleCloudPlatform/gcloud-java-examples](https://github.com/GoogleCloudPlatform/gcloud-java-examples/tree/master/test-apps). To test releases and platform-specific bugs, it's valuable to deploy the apps in that repository on App Engine, Compute Engine, and from your own desktop. + +#### Example application + +The example application should be a simple command line interface for the service. It should use common library usage patterns so that users can have good examples of how to use the service. Be sure to keep the examples up to date if/when there are updates that make the API cleaner and more concise. See examples of applications under the `gcloud-java-examples` folder. The example application should be in the package `com.google.cloud.examples.servicename`. + +#### Documentation + +* Include a summary of the service and code snippets on the main repository's README. These snippets should be simple and cover a few common usage patterns. The README snippets should also be added to `gcloud-java-examples` in the package `com.google.cloud.examples.servicename.snippets`. Placing snippet code in the repository ensures that the snippet code builds when Travis CI is run. For this purpose, README snippets and the snippet code in `gcloud-java-examples` should be kept in sync. Issue #753 suggests autogenerating READMEs, which would be useful for keeping snippet code in sync. As of yet, we do not have unit tests for snippets, so the snippets should be tested periodically, especially after any relevant library updates. +* Create a README in the service's folder. This README should mimic the structure of other services' READMEs. In particular, you should create a step-by-step "Getting Started" guide. See [`gcloud-java-datastore`'s README](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-datastore/README.md) for reference. All code in that step-by-step guide should also be included in the `gcloud-java-examples` snippets package. +* The API and test helper packages should have `package-info.java` files. These files should contain descriptions of the packages as well as simple example code and/or links to code snippets. +* Public methods, classes, and builders should contain meaningful Javadoc. When possible, copy docs from the service's `cloud.google.com` API documentation and provide `@see` links to relevant Google Cloud web pages. Document both unchecked and checked exceptions. +* Update [`TESTING`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/TESTING.md) with how to run tests using the test helper. +* Update the [`gcloud-java-examples` README](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-examples/README.md) with instructions on how to run your example application. + +Notes/reminders: +* Clearly document which APIs must be enabled in the Developers Console's API Manager. +* Versioning in documentation is automatically updated by the script `utilities/update_docs_version.sh`. Be sure to examine that script to make sure any version-dependent documentation will be updated properly upon release. + +### Workflow + +New services should be created in a branch based on `master`. The branch name should include the suffix "-alpha". For example, while developing `gcloud-java-pubsub`, all Pub/Sub related work should be done in `pubsub-alpha`. All code should be submitted through pull requests from a branch on a forked repository. Limiting pull request size is very helpful for reviewers. All code that is merged into the branch should be standalone and well-tested. Any todo comments in the code should have an associated Github issue number for tracking purposes. You should periodically pull updates from the master branch, especially if there are project-wide updates or if relevant changes have been made to the core utilities library, `gcloud-java-core`. This PR should only contain commits related to the merge to ease code review. + +Create at least two milestones (stable and future) for your service and an issue tag with the service name. Create issues for any to-do items and tag them appropriately. This keeps an up-to-date short-term to-do list and also allows for longer term roadmaps. + +Be sure you've configured the base folder's `pom.xml` correctly. + * Add your module to the base directory's `pom.xml` file under the list of modules (in alphabetical order). + * Add your module to the javadoc packaging settings. See PR #802 for an example. + + + +When your service is complete, contact the service owners to get a review. The primary purpose of this review is to make sure that the gcloud-java service interacts with the Google Cloud service properly. Present the reviewers with a link to the Github repository, as well as your (updated) design document that details the API. + +### Closing remarks + +* Efforts should be made to maintain the current style of the repository and a consistent style between gcloud-java services. + * We anticipate that people will sometimes use multiple `gcloud-java` services, so we don't want differences in conventions from one service API to another. Look at existing `gcloud-java` services to see coding and naming conventions. + * Codacy is configured to report on pull requests about style issues. Whenever possible, those comments should be addressed. Coders and reviewers should also run a linter on pull requests, because the Codacy tool may not catch all style errors. There is a [Checkstyle configuration](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/checkstyle.xml) provided in the repository. +* When weighing which services to add, consider that a hand-crafted `gcloud-java` service API is especially useful if it can simplify the usability of the autogenerated client. diff --git a/TESTING.md b/TESTING.md index 3ad181310b17..7ae34db4c4f1 100644 --- a/TESTING.md +++ b/TESTING.md @@ -11,55 +11,98 @@ This library provides tools to help write tests for code that uses the following #### On your machine -You can test against a temporary local datastore by following these steps: +You can test against a temporary local Datastore by following these steps: -1. Start the local datastore emulator using `LocalGcdHelper`. This can be done in two ways: - - Run `LocalGcdHelper.java`'s `main` method with arguments `START` and (optionally) `--port=`. This will create a temporary folder on your computer and bind `localhost:` for communication with the local datastore. The port number is an optional argument. If no port number is specified, port 8080 will be used. - - Call `LocalGcdHelper.start(, )` before running your tests. Save the `LocalGcdHelper` object returned so that you can stop the emulator later. +1. Start the local Datastore emulator before running your tests using `LocalDatastoreHelper`'s `create` and `start` methods. This will create a temporary folder on your computer and bind a port for communication with the local Datastore. There is an optional argument for `create`: consistency. The consistency setting controls the fraction of Datastore writes that are immediately visible in global queries. + ```java + // Use the default consistency setting of 0.9 + LocalDatastoreHelper helper = LocalDatastoreHelper.create(); + // or explicitly set the consistency + helper = LocalDatastoreHelper.create(0.6); + + helper.start(); // Starts the local Datastore emulator in a separate process + ``` -2. In your program, create and use a datastore whose host is set host to `localhost:`. For example, +2. Create and use a `Datastore` object with the options given by the `LocalDatastoreHelper` instance. For example: ```java - DatastoreOptions options = DatastoreOptions.builder() - .projectId(PROJECT_ID) - .host("http://localhost:8080") - .build(); - Datastore localDatastore = options.service(); + Datastore localDatastore = helper.options().service(); ``` + 3. Run your tests. -4. Stop the local datastore emulator. - - If you ran `LocalGcdHelper.java`'s `main` function to start the emulator, run `LocalGcdHelper.java`'s `main` method with arguments `STOP` and (optionally) `--port=`. If the port is not supplied, the program will attempt to close the last port started. - - If you ran `LocalGcdHelper.start()` to start the emulator, call the `stop()` method on the `LocalGcdHelper` object returned by `LocalGcdHelper.start()`. +4. Stop the local datastore emulator by calling the `stop()` method, like so: + ```java + helper.stop(); + ``` #### On a remote machine -You can test against a remote datastore emulator as well. To do this, set the `DatastoreOptions` project endpoint to the hostname of the remote machine, like the example below. +You can test against a remote Datastore emulator as well. To do this, set the `DatastoreOptions` project endpoint to the hostname of the remote machine, like the example below. ```java DatastoreOptions options = DatastoreOptions.builder() - .projectId(PROJECT_ID) + .projectId("my-project-id") // must match project ID specified on remote machine .host("http://:") + .authCredentials(AuthCredentials.noAuth()) .build(); Datastore localDatastore = options.service(); ``` -Note that the remote datastore must be running before your tests are run. +We recommend that you start the emulator on the remote machine using the [Google Cloud SDK](https://cloud.google.com/sdk/gcloud/reference/beta/emulators/datastore/) from command line, as shown below: + +``` +gcloud beta emulators datastore start --host-port : +``` + +### Testing code that uses DNS + +#### On your machine + +You can test against an in-memory local DNS by following these steps: + +1. Before running your testing code, start the DNS emulator `LocalDnsHelper`. This can be done as follows: + + ```java + long delay = 0; + LocalDnsHelper helper = LocalDnsHelper.create(delay); + helper.start(); + ``` + + This will spawn a server thread that listens to `localhost` at an ephemeral port for DNS requests. + The `delay` parameter determines if change requests should be processed synchronously + (value `0`) or in a separate thread with a minimum of delay of `delay` milliseconds. + +2. In your program, create the DNS service by using the helper's `options()` method. For example: + + ```java + Dns dns = LocalDnsHelper.options().service(); + ``` + +3. Run your tests. + +4. Stop the DNS emulator. + + ```java + helper.stop(); + ``` + + This method will block until the server thread has been terminated. ### Testing code that uses Storage -Currently, there isn't an emulator for Google Cloud Storage, so an alternative is to create a test project. `RemoteGcsHelper` contains convenience methods to make setting up and cleaning up the test project easier. To use this class, follow the steps below: +Currently, there isn't an emulator for Google Cloud Storage, so an alternative is to create a test project. `RemoteStorageHelper` contains convenience methods to make setting up and cleaning up the test project easier. To use this class, follow the steps below: 1. Create a test Google Cloud project. 2. Download a JSON service account credentials file from the Google Developer's Console. See more about this on the [Google Cloud Platform Storage Authentication page][cloud-platform-storage-authentication]. -3. Create a `RemoteGcsHelper` object using your project ID and JSON key. -Here is an example that uses the `RemoteGcsHelper` to create a bucket. +3. Create a `RemoteStorageHelper` object using your project ID and JSON key. +Here is an example that uses the `RemoteStorageHelper` to create a bucket. ```java - RemoteGcsHelper gcsHelper = - RemoteGcsHelper.create(PROJECT_ID, new FileInputStream("/path/to/my/JSON/key.json")); - Storage storage = gcsHelper.options().service(); - String bucket = RemoteGcsHelper.generateBucketName(); + RemoteStorageHelper helper = + RemoteStorageHelper.create(PROJECT_ID, new FileInputStream("/path/to/my/JSON/key.json")); + Storage storage = helper.options().service(); + String bucket = RemoteStorageHelper.generateBucketName(); storage.create(BucketInfo.of(bucket)); ``` @@ -68,20 +111,18 @@ Here is an example that uses the `RemoteGcsHelper` to create a bucket. 5. Clean up the test project by using `forceDelete` to clear any buckets used. Here is an example that clears the bucket created in Step 3 with a timeout of 5 seconds. ```java - RemoteGcsHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS); + RemoteStorageHelper.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: +You can test against an in-memory 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(); ``` @@ -134,4 +175,4 @@ Here is an example that clears the dataset created in Step 3. ``` [cloud-platform-storage-authentication]:https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts -[create-service-account]:https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount \ No newline at end of file +[create-service-account]:https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount diff --git a/codacy-conf.json b/codacy-conf.json index e8c819684c9c..47998a3870d7 100644 --- a/codacy-conf.json +++ b/codacy-conf.json @@ -1 +1 @@ -{"patterns":[{"patternId":"Custom_Javascript_Scopes","enabled":true},{"patternId":"Custom_Javascript_EvalWith","enabled":true},{"patternId":"Custom_Javascript_TryCatch","enabled":true},{"patternId":"Custom_Scala_NonFatal","enabled":true},{"patternId":"bitwise","enabled":true},{"patternId":"maxparams","enabled":true},{"patternId":"CSSLint_universal_selector","enabled":true},{"patternId":"CSSLint_unqualified_attributes","enabled":true},{"patternId":"CSSLint_zero_units","enabled":true},{"patternId":"CSSLint_overqualified_elements","enabled":true},{"patternId":"CSSLint_shorthand","enabled":true},{"patternId":"CSSLint_duplicate_background_images","enabled":true},{"patternId":"CSSLint_box_model","enabled":true},{"patternId":"CSSLint_compatible_vendor_prefixes","enabled":true},{"patternId":"CSSLint_display_property_grouping","enabled":true},{"patternId":"CSSLint_duplicate_properties","enabled":true},{"patternId":"CSSLint_empty_rules","enabled":true},{"patternId":"CSSLint_errors","enabled":true},{"patternId":"CSSLint_gradients","enabled":true},{"patternId":"CSSLint_important","enabled":true},{"patternId":"CSSLint_known_properties","enabled":true},{"patternId":"CSSLint_text_indent","enabled":true},{"patternId":"CSSLint_unique_headings","enabled":true},{"patternId":"PyLint_E0100","enabled":true},{"patternId":"PyLint_E0101","enabled":true},{"patternId":"PyLint_E0102","enabled":true},{"patternId":"PyLint_E0103","enabled":true},{"patternId":"PyLint_E0104","enabled":true},{"patternId":"PyLint_E0105","enabled":true},{"patternId":"PyLint_E0106","enabled":true},{"patternId":"PyLint_E0107","enabled":true},{"patternId":"PyLint_E0108","enabled":true},{"patternId":"PyLint_E0202","enabled":true},{"patternId":"PyLint_E0203","enabled":true},{"patternId":"PyLint_E0211","enabled":true},{"patternId":"PyLint_E0601","enabled":true},{"patternId":"PyLint_E0603","enabled":true},{"patternId":"PyLint_E0604","enabled":true},{"patternId":"PyLint_E0701","enabled":true},{"patternId":"PyLint_E0702","enabled":true},{"patternId":"PyLint_E0710","enabled":true},{"patternId":"PyLint_E0711","enabled":true},{"patternId":"PyLint_E0712","enabled":true},{"patternId":"PyLint_E1003","enabled":true},{"patternId":"PyLint_E1102","enabled":true},{"patternId":"PyLint_E1111","enabled":true},{"patternId":"PyLint_E1120","enabled":true},{"patternId":"PyLint_E1121","enabled":true},{"patternId":"PyLint_E1123","enabled":true},{"patternId":"PyLint_E1124","enabled":true},{"patternId":"PyLint_E1200","enabled":true},{"patternId":"PyLint_E1201","enabled":true},{"patternId":"PyLint_E1205","enabled":true},{"patternId":"PyLint_E1206","enabled":true},{"patternId":"PyLint_E1300","enabled":true},{"patternId":"PyLint_E1301","enabled":true},{"patternId":"PyLint_E1302","enabled":true},{"patternId":"PyLint_E1303","enabled":true},{"patternId":"PyLint_E1304","enabled":true},{"patternId":"PyLint_E1305","enabled":true},{"patternId":"PyLint_E1306","enabled":true},{"patternId":"rulesets-codesize.xml-CyclomaticComplexity","enabled":true},{"patternId":"rulesets-codesize.xml-NPathComplexity","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveMethodLength","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveClassLength","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveParameterList","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessivePublicCount","enabled":true},{"patternId":"rulesets-codesize.xml-TooManyFields","enabled":true},{"patternId":"rulesets-codesize.xml-TooManyMethods","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveClassComplexity","enabled":true},{"patternId":"rulesets-controversial.xml-Superglobals","enabled":true},{"patternId":"rulesets-design.xml-ExitExpression","enabled":true},{"patternId":"rulesets-design.xml-EvalExpression","enabled":true},{"patternId":"rulesets-design.xml-GotoStatement","enabled":true},{"patternId":"rulesets-design.xml-NumberOfChildren","enabled":true},{"patternId":"rulesets-design.xml-DepthOfInheritance","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedPrivateField","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedLocalVariable","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedPrivateMethod","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedFormalParameter","enabled":true},{"patternId":"PyLint_C0303","enabled":true},{"patternId":"PyLint_C1001","enabled":true},{"patternId":"rulesets-naming.xml-ShortVariable","enabled":true},{"patternId":"rulesets-naming.xml-LongVariable","enabled":true},{"patternId":"rulesets-naming.xml-ShortMethodName","enabled":true},{"patternId":"rulesets-naming.xml-ConstantNamingConventions","enabled":true},{"patternId":"rulesets-naming.xml-BooleanGetMethodName","enabled":true},{"patternId":"PyLint_W0101","enabled":true},{"patternId":"PyLint_W0102","enabled":true},{"patternId":"PyLint_W0104","enabled":true},{"patternId":"PyLint_W0105","enabled":true},{"patternId":"Custom_Scala_GetCalls","enabled":true},{"patternId":"ScalaStyle_EqualsHashCodeChecker","enabled":true},{"patternId":"ScalaStyle_ParameterNumberChecker","enabled":true},{"patternId":"ScalaStyle_ReturnChecker","enabled":true},{"patternId":"ScalaStyle_NullChecker","enabled":true},{"patternId":"ScalaStyle_NoCloneChecker","enabled":true},{"patternId":"ScalaStyle_NoFinalizeChecker","enabled":true},{"patternId":"ScalaStyle_CovariantEqualsChecker","enabled":true},{"patternId":"ScalaStyle_StructuralTypeChecker","enabled":true},{"patternId":"ScalaStyle_MethodLengthChecker","enabled":true},{"patternId":"ScalaStyle_NumberOfMethodsInTypeChecker","enabled":true},{"patternId":"ScalaStyle_WhileChecker","enabled":true},{"patternId":"ScalaStyle_VarFieldChecker","enabled":true},{"patternId":"ScalaStyle_VarLocalChecker","enabled":true},{"patternId":"ScalaStyle_RedundantIfChecker","enabled":true},{"patternId":"ScalaStyle_DeprecatedJavaChecker","enabled":true},{"patternId":"ScalaStyle_EmptyClassChecker","enabled":true},{"patternId":"ScalaStyle_NotImplementedErrorUsage","enabled":true},{"patternId":"Custom_Scala_GroupImports","enabled":true},{"patternId":"Custom_Scala_ReservedKeywords","enabled":true},{"patternId":"Custom_Scala_ElseIf","enabled":true},{"patternId":"Custom_Scala_CallByNameAsLastArguments","enabled":true},{"patternId":"Custom_Scala_WildcardImportOnMany","enabled":true},{"patternId":"Custom_Scala_UtilTryForTryCatch","enabled":true},{"patternId":"Custom_Scala_ProhibitObjectName","enabled":true},{"patternId":"Custom_Scala_ImportsAtBeginningOfPackage","enabled":true},{"patternId":"Custom_Scala_NameResultsAndParameters","enabled":true},{"patternId":"Custom_Scala_IncompletePatternMatching","enabled":true},{"patternId":"Custom_Scala_UsefulTypeAlias","enabled":true},{"patternId":"Custom_Scala_JavaThreads","enabled":true},{"patternId":"Custom_Scala_DirectPromiseCreation","enabled":true},{"patternId":"Custom_Scala_StructuralTypes","enabled":true},{"patternId":"Custom_Scala_CollectionLastHead","enabled":true},{"patternId":"PyLint_W0106","enabled":true},{"patternId":"PyLint_W0107","enabled":true},{"patternId":"PyLint_W0108","enabled":true},{"patternId":"PyLint_W0109","enabled":true},{"patternId":"PyLint_W0110","enabled":true},{"patternId":"PyLint_W0120","enabled":true},{"patternId":"PyLint_W0122","enabled":true},{"patternId":"PyLint_W0150","enabled":true},{"patternId":"PyLint_W0199","enabled":true},{"patternId":"rulesets-cleancode.xml-ElseExpression","enabled":true},{"patternId":"rulesets-cleancode.xml-StaticAccess","enabled":true},{"patternId":"ScalaStyle_NonASCIICharacterChecker","enabled":true},{"patternId":"ScalaStyle_FieldNamesChecker","enabled":true},{"patternId":"Custom_Scala_WithNameCalls","enabled":true},{"patternId":"strictexception_AvoidRethrowingException","enabled":true},{"patternId":"strings_AppendCharacterWithChar","enabled":true},{"patternId":"braces_IfElseStmtsMustUseBraces","enabled":true},{"patternId":"basic_AvoidDecimalLiteralsInBigDecimalConstructor","enabled":true},{"patternId":"basic_CheckSkipResult","enabled":true},{"patternId":"javabeans_MissingSerialVersionUID","enabled":true},{"patternId":"migrating_ShortInstantiation","enabled":true},{"patternId":"design_AvoidInstanceofChecksInCatchClause","enabled":true},{"patternId":"naming_LongVariable","enabled":true},{"patternId":"migrating_ReplaceEnumerationWithIterator","enabled":true},{"patternId":"j2ee_DoNotCallSystemExit","enabled":true},{"patternId":"unusedcode_UnusedLocalVariable","enabled":true},{"patternId":"strings_InefficientStringBuffering","enabled":true},{"patternId":"basic_DontUseFloatTypeForLoopIndices","enabled":true},{"patternId":"basic_AvoidBranchingStatementAsLastInLoop","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseTestAnnotation","enabled":true},{"patternId":"optimizations_AddEmptyString","enabled":true},{"patternId":"logging-jakarta-commons_ProperLogger","enabled":true},{"patternId":"optimizations_RedundantFieldInitializer","enabled":true},{"patternId":"logging-java_AvoidPrintStackTrace","enabled":true},{"patternId":"empty_EmptyFinallyBlock","enabled":true},{"patternId":"design_CompareObjectsWithEquals","enabled":true},{"patternId":"basic_ClassCastExceptionWithToArray","enabled":true},{"patternId":"strictexception_DoNotExtendJavaLangError","enabled":true},{"patternId":"junit_UnnecessaryBooleanAssertion","enabled":true},{"patternId":"design_SimplifyBooleanExpressions","enabled":true},{"patternId":"basic_ForLoopShouldBeWhileLoop","enabled":true},{"patternId":"basic_BigIntegerInstantiation","enabled":true},{"patternId":"optimizations_UseArrayListInsteadOfVector","enabled":true},{"patternId":"optimizations_UnnecessaryWrapperObjectCreation","enabled":true},{"patternId":"strings_StringBufferInstantiationWithChar","enabled":true},{"patternId":"basic_JumbledIncrementer","enabled":true},{"patternId":"design_SwitchStmtsShouldHaveDefault","enabled":true},{"patternId":"strictexception_AvoidThrowingRawExceptionTypes","enabled":true},{"patternId":"migrating_LongInstantiation","enabled":true},{"patternId":"design_SimplifyBooleanReturns","enabled":true},{"patternId":"empty_EmptyInitializer","enabled":true},{"patternId":"design_FieldDeclarationsShouldBeAtStartOfClass","enabled":true},{"patternId":"unnecessary_UnnecessaryConversionTemporary","enabled":true},{"patternId":"design_AvoidProtectedFieldInFinalClass","enabled":true},{"patternId":"junit_UseAssertTrueInsteadOfAssertEquals","enabled":true},{"patternId":"naming_PackageCase","enabled":true},{"patternId":"migrating_JUnitUseExpected","enabled":true},{"patternId":"controversial_UnnecessaryConstructor","enabled":true},{"patternId":"naming_MethodNamingConventions","enabled":true},{"patternId":"design_DefaultLabelNotLastInSwitchStmt","enabled":true},{"patternId":"basic_UnconditionalIfStatement","enabled":true},{"patternId":"design_SingularField","enabled":true},{"patternId":"design_AssignmentToNonFinalStatic","enabled":true},{"patternId":"braces_WhileLoopsMustUseBraces","enabled":true},{"patternId":"logging-java_SystemPrintln","enabled":true},{"patternId":"strings_UseStringBufferLength","enabled":true},{"patternId":"controversial_AvoidUsingNativeCode","enabled":true},{"patternId":"strictexception_AvoidLosingExceptionInformation","enabled":true},{"patternId":"imports_ImportFromSamePackage","enabled":true},{"patternId":"finalizers_AvoidCallingFinalize","enabled":true},{"patternId":"finalizers_FinalizeOverloaded","enabled":true},{"patternId":"naming_ClassNamingConventions","enabled":true},{"patternId":"logging-java_LoggerIsNotStaticFinal","enabled":true},{"patternId":"finalizers_FinalizeOnlyCallsSuperFinalize","enabled":true},{"patternId":"unnecessary_UselessOverridingMethod","enabled":true},{"patternId":"naming_SuspiciousConstantFieldName","enabled":true},{"patternId":"design_OptimizableToArrayCall","enabled":true},{"patternId":"imports_UnnecessaryFullyQualifiedName","enabled":true},{"patternId":"migrating_ReplaceHashtableWithMap","enabled":true},{"patternId":"unusedcode_UnusedPrivateField","enabled":true},{"patternId":"strings_UnnecessaryCaseChange","enabled":true},{"patternId":"migrating_IntegerInstantiation","enabled":true},{"patternId":"design_NonStaticInitializer","enabled":true},{"patternId":"design_MissingBreakInSwitch","enabled":true},{"patternId":"design_AvoidReassigningParameters","enabled":true},{"patternId":"basic_AvoidThreadGroup","enabled":true},{"patternId":"empty_EmptyCatchBlock","parameters":{"allowCommentedBlocks":"true"},"enabled":true},{"patternId":"codesize_ExcessiveParameterList","parameters":{"minimum":"8","violationSuppressRegex":"\"\"","violationSuppressXPath":"\"\""},"enabled":true},{"patternId":"naming_SuspiciousHashcodeMethodName","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseBeforeAnnotation","enabled":true},{"patternId":"design_UncommentedEmptyMethodBody","enabled":true},{"patternId":"basic_BrokenNullCheck","enabled":true},{"patternId":"strings_ConsecutiveLiteralAppends","enabled":true},{"patternId":"strings_StringInstantiation","enabled":true},{"patternId":"design_EqualsNull","enabled":true},{"patternId":"basic_OverrideBothEqualsAndHashcode","enabled":true},{"patternId":"design_InstantiationToGetClass","enabled":true},{"patternId":"basic_BooleanInstantiation","enabled":true},{"patternId":"strings_AvoidStringBufferField","enabled":true},{"patternId":"basic_ReturnFromFinallyBlock","enabled":true},{"patternId":"empty_EmptyTryBlock","enabled":true},{"patternId":"naming_SuspiciousEqualsMethodName","enabled":true},{"patternId":"basic_ExtendsObject","enabled":true},{"patternId":"strings_UselessStringValueOf","enabled":true},{"patternId":"design_UnsynchronizedStaticDateFormatter","enabled":true},{"patternId":"design_UseCollectionIsEmpty","enabled":true},{"patternId":"controversial_AvoidFinalLocalVariable","enabled":true},{"patternId":"strictexception_AvoidThrowingNullPointerException","enabled":true},{"patternId":"design_AvoidProtectedMethodInFinalClassNotExtending","enabled":true},{"patternId":"optimizations_PrematureDeclaration","enabled":true},{"patternId":"empty_EmptySwitchStatements","enabled":true},{"patternId":"basic_MisplacedNullCheck","enabled":true},{"patternId":"optimizations_UseStringBufferForStringAppends","enabled":true},{"patternId":"strings_StringToString","enabled":true},{"patternId":"naming_MethodWithSameNameAsEnclosingClass","enabled":true},{"patternId":"migrating_ReplaceVectorWithList","enabled":true},{"patternId":"imports_UnusedImports","enabled":true},{"patternId":"unnecessary_UnnecessaryFinalModifier","enabled":true},{"patternId":"basic_AvoidMultipleUnaryOperators","enabled":true},{"patternId":"junit_SimplifyBooleanAssertion","enabled":true},{"patternId":"unnecessary_UselessParentheses","enabled":true},{"patternId":"design_IdempotentOperations","enabled":true},{"patternId":"braces_IfStmtsMustUseBraces","enabled":true},{"patternId":"strings_UseIndexOfChar","enabled":true},{"patternId":"naming_NoPackage","enabled":true},{"patternId":"finalizers_FinalizeDoesNotCallSuperFinalize","enabled":true},{"patternId":"design_UseVarargs","enabled":true},{"patternId":"unusedcode_UnusedFormalParameter","enabled":true},{"patternId":"design_ReturnEmptyArrayRatherThanNull","enabled":true},{"patternId":"junit_UseAssertNullInsteadOfAssertTrue","enabled":true},{"patternId":"design_UseUtilityClass","enabled":true},{"patternId":"design_AvoidDeeplyNestedIfStmts","enabled":true},{"patternId":"empty_EmptyStatementNotInLoop","enabled":true},{"patternId":"junit_UseAssertSameInsteadOfAssertTrue","enabled":true},{"patternId":"braces_ForLoopsMustUseBraces","enabled":true},{"patternId":"controversial_DoNotCallGarbageCollectionExplicitly","enabled":true},{"patternId":"naming_GenericsNaming","enabled":true},{"patternId":"strings_UseEqualsToCompareStrings","enabled":true},{"patternId":"optimizations_AvoidArrayLoops","enabled":true},{"patternId":"empty_EmptyStaticInitializer","enabled":true},{"patternId":"design_UncommentedEmptyConstructor","enabled":true},{"patternId":"empty_EmptyStatementBlock","enabled":true},{"patternId":"basic_CollapsibleIfStatements","enabled":true},{"patternId":"design_FinalFieldCouldBeStatic","enabled":true},{"patternId":"logging-java_MoreThanOneLogger","enabled":true},{"patternId":"codesize_ExcessiveClassLength","enabled":true},{"patternId":"design_ImmutableField","enabled":true},{"patternId":"controversial_OneDeclarationPerLine","enabled":true},{"patternId":"empty_EmptyWhileStmt","enabled":true},{"patternId":"unnecessary_UnnecessaryReturn","enabled":true},{"patternId":"strings_InefficientEmptyStringCheck","enabled":true},{"patternId":"design_UseNotifyAllInsteadOfNotify","enabled":true},{"patternId":"strictexception_DoNotThrowExceptionInFinally","enabled":true},{"patternId":"junit_UseAssertEqualsInsteadOfAssertTrue","enabled":true},{"patternId":"typeresolution_CloneMethodMustImplementCloneable","enabled":true},{"patternId":"codesize_NPathComplexity","enabled":true},{"patternId":"imports_DontImportJavaLang","enabled":true},{"patternId":"empty_EmptySynchronizedBlock","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseAfterAnnotation","enabled":true},{"patternId":"design_AvoidConstantsInterface","enabled":true},{"patternId":"unnecessary_UselessOperationOnImmutable","enabled":true},{"patternId":"design_PositionLiteralsFirstInComparisons","enabled":true},{"patternId":"migrating_ByteInstantiation","enabled":true},{"patternId":"junit_JUnitSpelling","enabled":true},{"patternId":"junit_JUnitTestsShouldIncludeAssert","enabled":true},{"patternId":"finalizers_EmptyFinalizer","enabled":true},{"patternId":"design_NonCaseLabelInSwitchStatement","enabled":true},{"patternId":"android_DoNotHardCodeSDCard","enabled":true},{"patternId":"design_LogicInversion","enabled":true},{"patternId":"unusedcode_UnusedPrivateMethod","enabled":true},{"patternId":"naming_AvoidDollarSigns","enabled":true},{"patternId":"finalizers_FinalizeShouldBeProtected","enabled":true},{"patternId":"clone_ProperCloneImplementation","enabled":true},{"patternId":"basic_CheckResultSet","enabled":true},{"patternId":"controversial_AvoidPrefixingMethodParameters","enabled":true},{"patternId":"migrating_JUnit4SuitesShouldUseSuiteAnnotation","enabled":true},{"patternId":"empty_EmptyIfStmt","enabled":true},{"patternId":"basic_DontCallThreadRun","enabled":true},{"patternId":"junit_JUnitStaticSuite","enabled":true},{"patternId":"optimizations_UseArraysAsList","enabled":true},{"patternId":"design_MissingStaticMethodInNonInstantiatableClass","enabled":true},{"patternId":"unusedcode_UnusedModifier","enabled":true},{"patternId":"Style_MethodName","enabled":true},{"patternId":"Metrics_CyclomaticComplexity","enabled":true},{"patternId":"Lint_DuplicateMethods","enabled":true},{"patternId":"Style_Lambda","enabled":true},{"patternId":"Lint_UselessSetterCall","enabled":true},{"patternId":"Style_VariableName","enabled":true},{"patternId":"Lint_AmbiguousOperator","enabled":true},{"patternId":"Style_LeadingCommentSpace","enabled":true},{"patternId":"Style_CaseEquality","enabled":true},{"patternId":"Lint_StringConversionInInterpolation","enabled":true},{"patternId":"Performance_ReverseEach","enabled":true},{"patternId":"Lint_LiteralInCondition","enabled":true},{"patternId":"Performance_Sample","enabled":true},{"patternId":"Style_NonNilCheck","enabled":true},{"patternId":"Lint_RescueException","enabled":true},{"patternId":"Lint_UselessElseWithoutRescue","enabled":true},{"patternId":"Style_ConstantName","enabled":true},{"patternId":"Lint_LiteralInInterpolation","enabled":true},{"patternId":"Lint_NestedMethodDefinition","enabled":true},{"patternId":"Style_DoubleNegation","enabled":true},{"patternId":"Lint_SpaceBeforeFirstArg","enabled":true},{"patternId":"Lint_Debugger","enabled":true},{"patternId":"Style_ClassVars","enabled":true},{"patternId":"Lint_EmptyEnsure","enabled":true},{"patternId":"Style_MultilineBlockLayout","enabled":true},{"patternId":"Lint_UnusedBlockArgument","enabled":true},{"patternId":"Lint_UselessAccessModifier","enabled":true},{"patternId":"Performance_Size","enabled":true},{"patternId":"Lint_EachWithObjectArgument","enabled":true},{"patternId":"Style_Alias","enabled":true},{"patternId":"Lint_Loop","enabled":true},{"patternId":"Style_NegatedWhile","enabled":true},{"patternId":"Style_ColonMethodCall","enabled":true},{"patternId":"Lint_AmbiguousRegexpLiteral","enabled":true},{"patternId":"Lint_UnusedMethodArgument","enabled":true},{"patternId":"Style_MultilineIfThen","enabled":true},{"patternId":"Lint_EnsureReturn","enabled":true},{"patternId":"Style_NegatedIf","enabled":true},{"patternId":"Lint_Eval","enabled":true},{"patternId":"Style_NilComparison","enabled":true},{"patternId":"Style_ArrayJoin","enabled":true},{"patternId":"Lint_ConditionPosition","enabled":true},{"patternId":"Lint_UnreachableCode","enabled":true},{"patternId":"Performance_Count","enabled":true},{"patternId":"Lint_EmptyInterpolation","enabled":true},{"patternId":"Style_LambdaCall","enabled":true},{"patternId":"Lint_HandleExceptions","enabled":true},{"patternId":"Lint_ShadowingOuterLocalVariable","enabled":true},{"patternId":"Lint_EndAlignment","enabled":true},{"patternId":"Style_MultilineTernaryOperator","enabled":true},{"patternId":"Style_AutoResourceCleanup","enabled":true},{"patternId":"Lint_ElseLayout","enabled":true},{"patternId":"Style_NestedTernaryOperator","enabled":true},{"patternId":"Style_OneLineConditional","enabled":true},{"patternId":"Style_EmptyElse","enabled":true},{"patternId":"Lint_UselessComparison","enabled":true},{"patternId":"Metrics_PerceivedComplexity","enabled":true},{"patternId":"Style_InfiniteLoop","enabled":true},{"patternId":"Rails_Date","enabled":true},{"patternId":"Style_EvenOdd","enabled":true},{"patternId":"Style_IndentationConsistency","enabled":true},{"patternId":"Style_ModuleFunction","enabled":true},{"patternId":"Lint_UselessAssignment","enabled":true},{"patternId":"Style_EachWithObject","enabled":true},{"patternId":"Performance_Detect","enabled":true},{"patternId":"duplicate_key","enabled":true},{"patternId":"no_interpolation_in_single_quotes","enabled":true},{"patternId":"no_backticks","enabled":true},{"patternId":"no_unnecessary_fat_arrows","enabled":true},{"patternId":"indentation","enabled":true},{"patternId":"ensure_comprehensions","enabled":true},{"patternId":"no_stand_alone_at","enabled":true},{"patternId":"cyclomatic_complexity","enabled":true},{"patternId":"Deserialize","enabled":true},{"patternId":"SymbolDoS","enabled":true},{"patternId":"SkipBeforeFilter","enabled":true},{"patternId":"SanitizeMethods","enabled":true},{"patternId":"SelectTag","enabled":true},{"patternId":"XMLDoS","enabled":true},{"patternId":"SimpleFormat","enabled":true},{"patternId":"Evaluation","enabled":true},{"patternId":"BasicAuth","enabled":true},{"patternId":"JRubyXML","enabled":true},{"patternId":"RenderInline","enabled":true},{"patternId":"YAMLParsing","enabled":true},{"patternId":"Redirect","enabled":true},{"patternId":"UnsafeReflection","enabled":true},{"patternId":"SSLVerify","enabled":true},{"patternId":"HeaderDoS","enabled":true},{"patternId":"TranslateBug","enabled":true},{"patternId":"Execute","enabled":true},{"patternId":"JSONParsing","enabled":true},{"patternId":"LinkTo","enabled":true},{"patternId":"FileDisclosure","enabled":true},{"patternId":"SafeBufferManipulation","enabled":true},{"patternId":"ModelAttributes","enabled":true},{"patternId":"ResponseSplitting","enabled":true},{"patternId":"DigestDoS","enabled":true},{"patternId":"Send","enabled":true},{"patternId":"MailTo","enabled":true},{"patternId":"SymbolDoSCVE","enabled":true},{"patternId":"StripTags","enabled":true},{"patternId":"MassAssignment","enabled":true},{"patternId":"RegexDoS","enabled":true},{"patternId":"SelectVulnerability","enabled":true},{"patternId":"FileAccess","enabled":true},{"patternId":"ContentTag","enabled":true},{"patternId":"SessionSettings","enabled":true},{"patternId":"FilterSkipping","enabled":true},{"patternId":"CreateWith","enabled":true},{"patternId":"JSONEncoding","enabled":true},{"patternId":"SQLCVEs","enabled":true},{"patternId":"ForgerySetting","enabled":true},{"patternId":"QuoteTableName","enabled":true},{"patternId":"I18nXSS","enabled":true},{"patternId":"WithoutProtection","enabled":true},{"patternId":"CrossSiteScripting","enabled":true},{"patternId":"SingleQuotes","enabled":true},{"patternId":"NestedAttributes","enabled":true},{"patternId":"DetailedExceptions","enabled":true},{"patternId":"LinkToHref","enabled":true},{"patternId":"RenderDoS","enabled":true},{"patternId":"ModelSerialize","enabled":true},{"patternId":"SQL","enabled":true},{"patternId":"Render","enabled":true},{"patternId":"UnscopedFind","enabled":true},{"patternId":"ValidationRegex","enabled":true},{"patternId":"EscapeFunction","enabled":true},{"patternId":"Custom_Scala_FieldNamesChecker","enabled":true},{"patternId":"Custom_Scala_ObjDeserialization","enabled":true},{"patternId":"Custom_Scala_RSAPadding","enabled":true},{"patternId":"ESLint_no-extra-boolean-cast","enabled":true},{"patternId":"ESLint_no-iterator","enabled":true},{"patternId":"ESLint_no-invalid-regexp","enabled":true},{"patternId":"ESLint_no-obj-calls","enabled":true},{"patternId":"ESLint_no-sparse-arrays","enabled":true},{"patternId":"ESLint_no-unreachable","enabled":true},{"patternId":"ESLint_no-dupe-keys","enabled":true},{"patternId":"ESLint_no-multi-str","enabled":true},{"patternId":"ESLint_no-extend-native","enabled":true},{"patternId":"ESLint_guard-for-in","enabled":true},{"patternId":"ESLint_no-func-assign","enabled":true},{"patternId":"ESLint_no-extra-semi","enabled":true},{"patternId":"ESLint_camelcase","enabled":true},{"patternId":"ESLint_no-mixed-spaces-and-tabs","enabled":true},{"patternId":"ESLint_no-undef","enabled":true},{"patternId":"ESLint_semi","enabled":true},{"patternId":"ESLint_no-empty-character-class","enabled":true},{"patternId":"ESLint_complexity","enabled":true},{"patternId":"ESLint_no-dupe-class-members","enabled":true},{"patternId":"ESLint_no-debugger","enabled":true},{"patternId":"ESLint_block-scoped-var","enabled":true},{"patternId":"ESLint_no-loop-func","enabled":true},{"patternId":"ESLint_no-use-before-define","enabled":true},{"patternId":"ESLint_no-console","enabled":true},{"patternId":"ESLint_require-yield","enabled":true},{"patternId":"ESLint_no-redeclare","enabled":true},{"patternId":"ESLint_no-undefined","enabled":true},{"patternId":"ESLint_use-isnan","enabled":true},{"patternId":"ESLint_no-control-regex","enabled":true},{"patternId":"ESLint_no-const-assign","enabled":true},{"patternId":"ESLint_no-new","enabled":true},{"patternId":"ESLint_new-cap","enabled":true},{"patternId":"ESLint_no-irregular-whitespace","enabled":true},{"patternId":"ESLint_object-shorthand","enabled":true},{"patternId":"ESLint_no-ex-assign","enabled":true},{"patternId":"ESLint_wrap-iife","enabled":true},{"patternId":"ESLint_arrow-parens","enabled":true},{"patternId":"ESLint_no-constant-condition","enabled":true},{"patternId":"ESLint_no-octal","enabled":true},{"patternId":"ESLint_no-dupe-args","enabled":true},{"patternId":"ESLint_quotes","enabled":true},{"patternId":"ESLint_no-fallthrough","enabled":true},{"patternId":"ESLint_no-delete-var","enabled":true},{"patternId":"ESLint_no-caller","enabled":true},{"patternId":"ESLint_no-cond-assign","enabled":true},{"patternId":"ESLint_no-this-before-super","enabled":true},{"patternId":"ESLint_no-negated-in-lhs","enabled":true},{"patternId":"ESLint_no-inner-declarations","enabled":true},{"patternId":"ESLint_eqeqeq","enabled":true},{"patternId":"ESLint_curly","enabled":true},{"patternId":"ESLint_arrow-spacing","enabled":true},{"patternId":"ESLint_no-empty","enabled":true},{"patternId":"ESLint_no-unused-vars","enabled":true},{"patternId":"ESLint_generator-star-spacing","enabled":true},{"patternId":"ESLint_no-duplicate-case","enabled":true},{"patternId":"ESLint_valid-typeof","enabled":true},{"patternId":"ESLint_no-regex-spaces","enabled":true},{"patternId":"ESLint_no-class-assign","enabled":true},{"patternId":"PyLint_W0221","enabled":true},{"patternId":"PyLint_E0117","enabled":true},{"patternId":"PyLint_E0001","enabled":true},{"patternId":"PyLint_E0241","enabled":true},{"patternId":"PyLint_W0404","enabled":true},{"patternId":"PyLint_E0704","enabled":true},{"patternId":"PyLint_E0703","enabled":true},{"patternId":"PyLint_E0302","enabled":true},{"patternId":"PyLint_W1301","enabled":true},{"patternId":"PyLint_R0201","enabled":true},{"patternId":"PyLint_E0113","enabled":true},{"patternId":"PyLint_W0410","enabled":true},{"patternId":"PyLint_C0123","enabled":true},{"patternId":"PyLint_E0115","enabled":true},{"patternId":"PyLint_E0114","enabled":true},{"patternId":"PyLint_E1126","enabled":true},{"patternId":"PyLint_W0702","enabled":true},{"patternId":"PyLint_W1303","enabled":true},{"patternId":"PyLint_W0622","enabled":true},{"patternId":"PyLint_W0222","enabled":true},{"patternId":"PyLint_W0233","enabled":true},{"patternId":"PyLint_W1305","enabled":true},{"patternId":"PyLint_E1127","enabled":true},{"patternId":"PyLint_E0112","enabled":true},{"patternId":"PyLint_W0611","enabled":true},{"patternId":"PyLint_W0601","enabled":true},{"patternId":"PyLint_W1300","enabled":true},{"patternId":"PyLint_W0124","enabled":true},{"patternId":"PyLint_R0203","enabled":true},{"patternId":"PyLint_E0236","enabled":true},{"patternId":"PyLint_W0612","enabled":true},{"patternId":"PyLint_W0604","enabled":true},{"patternId":"PyLint_W0705","enabled":true},{"patternId":"PyLint_E0238","enabled":true},{"patternId":"PyLint_W0602","enabled":true},{"patternId":"PyLint_R0102","enabled":true},{"patternId":"PyLint_R0202","enabled":true},{"patternId":"PyLint_E0240","enabled":true},{"patternId":"PyLint_W0623","enabled":true},{"patternId":"PyLint_W0711","enabled":true},{"patternId":"PyLint_E0116","enabled":true},{"patternId":"PyLint_E0239","enabled":true},{"patternId":"PyLint_E1132","enabled":true},{"patternId":"PyLint_W1307","enabled":true},{"patternId":"PyLint_C0200","enabled":true},{"patternId":"PyLint_E0301","enabled":true},{"patternId":"PyLint_W1306","enabled":true},{"patternId":"PyLint_W1302","enabled":true},{"patternId":"PyLint_E0110","enabled":true},{"patternId":"PyLint_E1125","enabled":true}]} \ No newline at end of file +{"patterns":[{"patternId":"Custom_Javascript_Scopes","enabled":true},{"patternId":"Custom_Javascript_EvalWith","enabled":true},{"patternId":"Custom_Javascript_TryCatch","enabled":true},{"patternId":"Custom_Scala_NonFatal","enabled":true},{"patternId":"bitwise","enabled":true},{"patternId":"maxparams","enabled":true},{"patternId":"CSSLint_universal_selector","enabled":true},{"patternId":"CSSLint_unqualified_attributes","enabled":true},{"patternId":"CSSLint_zero_units","enabled":true},{"patternId":"CSSLint_overqualified_elements","enabled":true},{"patternId":"CSSLint_shorthand","enabled":true},{"patternId":"CSSLint_duplicate_background_images","enabled":true},{"patternId":"CSSLint_box_model","enabled":true},{"patternId":"CSSLint_compatible_vendor_prefixes","enabled":true},{"patternId":"CSSLint_display_property_grouping","enabled":true},{"patternId":"CSSLint_duplicate_properties","enabled":true},{"patternId":"CSSLint_empty_rules","enabled":true},{"patternId":"CSSLint_errors","enabled":true},{"patternId":"CSSLint_gradients","enabled":true},{"patternId":"CSSLint_important","enabled":true},{"patternId":"CSSLint_known_properties","enabled":true},{"patternId":"CSSLint_text_indent","enabled":true},{"patternId":"CSSLint_unique_headings","enabled":true},{"patternId":"PyLint_E0100","enabled":true},{"patternId":"PyLint_E0101","enabled":true},{"patternId":"PyLint_E0102","enabled":true},{"patternId":"PyLint_E0103","enabled":true},{"patternId":"PyLint_E0104","enabled":true},{"patternId":"PyLint_E0105","enabled":true},{"patternId":"PyLint_E0106","enabled":true},{"patternId":"PyLint_E0107","enabled":true},{"patternId":"PyLint_E0108","enabled":true},{"patternId":"PyLint_E0202","enabled":true},{"patternId":"PyLint_E0203","enabled":true},{"patternId":"PyLint_E0211","enabled":true},{"patternId":"PyLint_E0601","enabled":true},{"patternId":"PyLint_E0603","enabled":true},{"patternId":"PyLint_E0604","enabled":true},{"patternId":"PyLint_E0701","enabled":true},{"patternId":"PyLint_E0702","enabled":true},{"patternId":"PyLint_E0710","enabled":true},{"patternId":"PyLint_E0711","enabled":true},{"patternId":"PyLint_E0712","enabled":true},{"patternId":"PyLint_E1003","enabled":true},{"patternId":"PyLint_E1102","enabled":true},{"patternId":"PyLint_E1111","enabled":true},{"patternId":"PyLint_E1120","enabled":true},{"patternId":"PyLint_E1121","enabled":true},{"patternId":"PyLint_E1123","enabled":true},{"patternId":"PyLint_E1124","enabled":true},{"patternId":"PyLint_E1200","enabled":true},{"patternId":"PyLint_E1201","enabled":true},{"patternId":"PyLint_E1205","enabled":true},{"patternId":"PyLint_E1206","enabled":true},{"patternId":"PyLint_E1300","enabled":true},{"patternId":"PyLint_E1301","enabled":true},{"patternId":"PyLint_E1302","enabled":true},{"patternId":"PyLint_E1303","enabled":true},{"patternId":"PyLint_E1304","enabled":true},{"patternId":"PyLint_E1305","enabled":true},{"patternId":"PyLint_E1306","enabled":true},{"patternId":"rulesets-codesize.xml-CyclomaticComplexity","enabled":true},{"patternId":"rulesets-codesize.xml-NPathComplexity","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveMethodLength","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveClassLength","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveParameterList","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessivePublicCount","enabled":true},{"patternId":"rulesets-codesize.xml-TooManyFields","enabled":true},{"patternId":"rulesets-codesize.xml-TooManyMethods","enabled":true},{"patternId":"rulesets-codesize.xml-ExcessiveClassComplexity","enabled":true},{"patternId":"rulesets-controversial.xml-Superglobals","enabled":true},{"patternId":"rulesets-design.xml-ExitExpression","enabled":true},{"patternId":"rulesets-design.xml-EvalExpression","enabled":true},{"patternId":"rulesets-design.xml-GotoStatement","enabled":true},{"patternId":"rulesets-design.xml-NumberOfChildren","enabled":true},{"patternId":"rulesets-design.xml-DepthOfInheritance","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedPrivateField","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedLocalVariable","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedPrivateMethod","enabled":true},{"patternId":"rulesets-unusedcode.xml-UnusedFormalParameter","enabled":true},{"patternId":"PyLint_C0303","enabled":true},{"patternId":"PyLint_C1001","enabled":true},{"patternId":"rulesets-naming.xml-ShortVariable","enabled":true},{"patternId":"rulesets-naming.xml-LongVariable","enabled":true},{"patternId":"rulesets-naming.xml-ShortMethodName","enabled":true},{"patternId":"rulesets-naming.xml-ConstantNamingConventions","enabled":true},{"patternId":"rulesets-naming.xml-BooleanGetMethodName","enabled":true},{"patternId":"PyLint_W0101","enabled":true},{"patternId":"PyLint_W0102","enabled":true},{"patternId":"PyLint_W0104","enabled":true},{"patternId":"PyLint_W0105","enabled":true},{"patternId":"Custom_Scala_GetCalls","enabled":true},{"patternId":"ScalaStyle_EqualsHashCodeChecker","enabled":true},{"patternId":"ScalaStyle_ParameterNumberChecker","enabled":true},{"patternId":"ScalaStyle_ReturnChecker","enabled":true},{"patternId":"ScalaStyle_NullChecker","enabled":true},{"patternId":"ScalaStyle_NoCloneChecker","enabled":true},{"patternId":"ScalaStyle_NoFinalizeChecker","enabled":true},{"patternId":"ScalaStyle_CovariantEqualsChecker","enabled":true},{"patternId":"ScalaStyle_StructuralTypeChecker","enabled":true},{"patternId":"ScalaStyle_MethodLengthChecker","enabled":true},{"patternId":"ScalaStyle_NumberOfMethodsInTypeChecker","enabled":true},{"patternId":"ScalaStyle_WhileChecker","enabled":true},{"patternId":"ScalaStyle_VarFieldChecker","enabled":true},{"patternId":"ScalaStyle_VarLocalChecker","enabled":true},{"patternId":"ScalaStyle_RedundantIfChecker","enabled":true},{"patternId":"ScalaStyle_DeprecatedJavaChecker","enabled":true},{"patternId":"ScalaStyle_EmptyClassChecker","enabled":true},{"patternId":"ScalaStyle_NotImplementedErrorUsage","enabled":true},{"patternId":"Custom_Scala_GroupImports","enabled":true},{"patternId":"Custom_Scala_ReservedKeywords","enabled":true},{"patternId":"Custom_Scala_ElseIf","enabled":true},{"patternId":"Custom_Scala_CallByNameAsLastArguments","enabled":true},{"patternId":"Custom_Scala_WildcardImportOnMany","enabled":true},{"patternId":"Custom_Scala_UtilTryForTryCatch","enabled":true},{"patternId":"Custom_Scala_ProhibitObjectName","enabled":true},{"patternId":"Custom_Scala_ImportsAtBeginningOfPackage","enabled":true},{"patternId":"Custom_Scala_NameResultsAndParameters","enabled":true},{"patternId":"Custom_Scala_IncompletePatternMatching","enabled":true},{"patternId":"Custom_Scala_UsefulTypeAlias","enabled":true},{"patternId":"Custom_Scala_JavaThreads","enabled":true},{"patternId":"Custom_Scala_DirectPromiseCreation","enabled":true},{"patternId":"Custom_Scala_StructuralTypes","enabled":true},{"patternId":"Custom_Scala_CollectionLastHead","enabled":true},{"patternId":"PyLint_W0106","enabled":true},{"patternId":"PyLint_W0107","enabled":true},{"patternId":"PyLint_W0108","enabled":true},{"patternId":"PyLint_W0109","enabled":true},{"patternId":"PyLint_W0110","enabled":true},{"patternId":"PyLint_W0120","enabled":true},{"patternId":"PyLint_W0122","enabled":true},{"patternId":"PyLint_W0150","enabled":true},{"patternId":"PyLint_W0199","enabled":true},{"patternId":"rulesets-cleancode.xml-ElseExpression","enabled":true},{"patternId":"rulesets-cleancode.xml-StaticAccess","enabled":true},{"patternId":"ScalaStyle_NonASCIICharacterChecker","enabled":true},{"patternId":"ScalaStyle_FieldNamesChecker","enabled":true},{"patternId":"Custom_Scala_WithNameCalls","enabled":true},{"patternId":"strictexception_AvoidRethrowingException","enabled":true},{"patternId":"strings_AppendCharacterWithChar","enabled":true},{"patternId":"braces_IfElseStmtsMustUseBraces","enabled":true},{"patternId":"basic_AvoidDecimalLiteralsInBigDecimalConstructor","enabled":true},{"patternId":"basic_CheckSkipResult","enabled":true},{"patternId":"javabeans_MissingSerialVersionUID","enabled":true},{"patternId":"migrating_ShortInstantiation","enabled":true},{"patternId":"design_AvoidInstanceofChecksInCatchClause","enabled":true},{"patternId":"migrating_ReplaceEnumerationWithIterator","enabled":true},{"patternId":"j2ee_DoNotCallSystemExit","enabled":true},{"patternId":"unusedcode_UnusedLocalVariable","enabled":true},{"patternId":"strings_InefficientStringBuffering","enabled":true},{"patternId":"basic_DontUseFloatTypeForLoopIndices","enabled":true},{"patternId":"basic_AvoidBranchingStatementAsLastInLoop","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseTestAnnotation","enabled":true},{"patternId":"optimizations_AddEmptyString","enabled":true},{"patternId":"logging-jakarta-commons_ProperLogger","enabled":true},{"patternId":"optimizations_RedundantFieldInitializer","enabled":true},{"patternId":"logging-java_AvoidPrintStackTrace","enabled":true},{"patternId":"empty_EmptyFinallyBlock","enabled":true},{"patternId":"design_CompareObjectsWithEquals","enabled":true},{"patternId":"basic_ClassCastExceptionWithToArray","enabled":true},{"patternId":"strictexception_DoNotExtendJavaLangError","enabled":true},{"patternId":"junit_UnnecessaryBooleanAssertion","enabled":true},{"patternId":"design_SimplifyBooleanExpressions","enabled":true},{"patternId":"basic_ForLoopShouldBeWhileLoop","enabled":true},{"patternId":"basic_BigIntegerInstantiation","enabled":true},{"patternId":"optimizations_UseArrayListInsteadOfVector","enabled":true},{"patternId":"optimizations_UnnecessaryWrapperObjectCreation","enabled":true},{"patternId":"strings_StringBufferInstantiationWithChar","enabled":true},{"patternId":"basic_JumbledIncrementer","enabled":true},{"patternId":"design_SwitchStmtsShouldHaveDefault","enabled":true},{"patternId":"strictexception_AvoidThrowingRawExceptionTypes","enabled":true},{"patternId":"migrating_LongInstantiation","enabled":true},{"patternId":"design_SimplifyBooleanReturns","enabled":true},{"patternId":"empty_EmptyInitializer","enabled":true},{"patternId":"design_FieldDeclarationsShouldBeAtStartOfClass","enabled":true},{"patternId":"unnecessary_UnnecessaryConversionTemporary","enabled":true},{"patternId":"design_AvoidProtectedFieldInFinalClass","enabled":true},{"patternId":"junit_UseAssertTrueInsteadOfAssertEquals","enabled":true},{"patternId":"naming_PackageCase","enabled":true},{"patternId":"migrating_JUnitUseExpected","enabled":true},{"patternId":"controversial_UnnecessaryConstructor","enabled":true},{"patternId":"naming_MethodNamingConventions","parameters":{"violationSuppressRegex":"\"\"","violationSuppressXPath":"\"//MethodDeclarator[contains(@Image, 'test')]\""},"enabled":true},{"patternId":"design_DefaultLabelNotLastInSwitchStmt","enabled":true},{"patternId":"basic_UnconditionalIfStatement","enabled":true},{"patternId":"design_SingularField","enabled":true},{"patternId":"design_AssignmentToNonFinalStatic","enabled":true},{"patternId":"braces_WhileLoopsMustUseBraces","enabled":true},{"patternId":"logging-java_SystemPrintln","enabled":true},{"patternId":"strings_UseStringBufferLength","enabled":true},{"patternId":"controversial_AvoidUsingNativeCode","enabled":true},{"patternId":"strictexception_AvoidLosingExceptionInformation","enabled":true},{"patternId":"imports_ImportFromSamePackage","enabled":true},{"patternId":"finalizers_AvoidCallingFinalize","enabled":true},{"patternId":"finalizers_FinalizeOverloaded","enabled":true},{"patternId":"naming_ClassNamingConventions","enabled":true},{"patternId":"logging-java_LoggerIsNotStaticFinal","enabled":true},{"patternId":"finalizers_FinalizeOnlyCallsSuperFinalize","enabled":true},{"patternId":"unnecessary_UselessOverridingMethod","enabled":true},{"patternId":"naming_SuspiciousConstantFieldName","enabled":true},{"patternId":"design_OptimizableToArrayCall","enabled":true},{"patternId":"imports_UnnecessaryFullyQualifiedName","enabled":true},{"patternId":"migrating_ReplaceHashtableWithMap","enabled":true},{"patternId":"unusedcode_UnusedPrivateField","enabled":true},{"patternId":"strings_UnnecessaryCaseChange","enabled":true},{"patternId":"migrating_IntegerInstantiation","enabled":true},{"patternId":"design_NonStaticInitializer","enabled":true},{"patternId":"design_MissingBreakInSwitch","enabled":true},{"patternId":"design_AvoidReassigningParameters","enabled":true},{"patternId":"basic_AvoidThreadGroup","enabled":true},{"patternId":"empty_EmptyCatchBlock","parameters":{"allowCommentedBlocks":"true"},"enabled":true},{"patternId":"codesize_ExcessiveParameterList","parameters":{"minimum":"8","violationSuppressRegex":"\"\"","violationSuppressXPath":"\"\""},"enabled":true},{"patternId":"naming_SuspiciousHashcodeMethodName","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseBeforeAnnotation","enabled":true},{"patternId":"design_UncommentedEmptyMethodBody","enabled":true},{"patternId":"basic_BrokenNullCheck","enabled":true},{"patternId":"strings_ConsecutiveLiteralAppends","enabled":true},{"patternId":"strings_StringInstantiation","enabled":true},{"patternId":"design_EqualsNull","enabled":true},{"patternId":"basic_OverrideBothEqualsAndHashcode","enabled":true},{"patternId":"design_InstantiationToGetClass","enabled":true},{"patternId":"basic_BooleanInstantiation","enabled":true},{"patternId":"strings_AvoidStringBufferField","enabled":true},{"patternId":"basic_ReturnFromFinallyBlock","enabled":true},{"patternId":"empty_EmptyTryBlock","enabled":true},{"patternId":"naming_SuspiciousEqualsMethodName","enabled":true},{"patternId":"basic_ExtendsObject","enabled":true},{"patternId":"strings_UselessStringValueOf","enabled":true},{"patternId":"design_UnsynchronizedStaticDateFormatter","enabled":true},{"patternId":"design_UseCollectionIsEmpty","enabled":true},{"patternId":"controversial_AvoidFinalLocalVariable","enabled":true},{"patternId":"strictexception_AvoidThrowingNullPointerException","enabled":true},{"patternId":"design_AvoidProtectedMethodInFinalClassNotExtending","enabled":true},{"patternId":"optimizations_PrematureDeclaration","enabled":true},{"patternId":"empty_EmptySwitchStatements","enabled":true},{"patternId":"basic_MisplacedNullCheck","enabled":true},{"patternId":"optimizations_UseStringBufferForStringAppends","enabled":true},{"patternId":"strings_StringToString","enabled":true},{"patternId":"naming_MethodWithSameNameAsEnclosingClass","enabled":true},{"patternId":"migrating_ReplaceVectorWithList","enabled":true},{"patternId":"imports_UnusedImports","enabled":true},{"patternId":"unnecessary_UnnecessaryFinalModifier","enabled":true},{"patternId":"basic_AvoidMultipleUnaryOperators","enabled":true},{"patternId":"junit_SimplifyBooleanAssertion","enabled":true},{"patternId":"unnecessary_UselessParentheses","enabled":true},{"patternId":"design_IdempotentOperations","enabled":true},{"patternId":"braces_IfStmtsMustUseBraces","enabled":true},{"patternId":"strings_UseIndexOfChar","enabled":true},{"patternId":"naming_NoPackage","enabled":true},{"patternId":"finalizers_FinalizeDoesNotCallSuperFinalize","enabled":true},{"patternId":"design_UseVarargs","enabled":true},{"patternId":"unusedcode_UnusedFormalParameter","enabled":true},{"patternId":"design_ReturnEmptyArrayRatherThanNull","enabled":true},{"patternId":"junit_UseAssertNullInsteadOfAssertTrue","enabled":true},{"patternId":"design_UseUtilityClass","enabled":true},{"patternId":"design_AvoidDeeplyNestedIfStmts","enabled":true},{"patternId":"empty_EmptyStatementNotInLoop","enabled":true},{"patternId":"junit_UseAssertSameInsteadOfAssertTrue","enabled":true},{"patternId":"braces_ForLoopsMustUseBraces","enabled":true},{"patternId":"controversial_DoNotCallGarbageCollectionExplicitly","enabled":true},{"patternId":"naming_GenericsNaming","enabled":true},{"patternId":"strings_UseEqualsToCompareStrings","enabled":true},{"patternId":"optimizations_AvoidArrayLoops","enabled":true},{"patternId":"empty_EmptyStaticInitializer","enabled":true},{"patternId":"design_UncommentedEmptyConstructor","enabled":true},{"patternId":"empty_EmptyStatementBlock","enabled":true},{"patternId":"basic_CollapsibleIfStatements","enabled":true},{"patternId":"design_FinalFieldCouldBeStatic","enabled":true},{"patternId":"logging-java_MoreThanOneLogger","enabled":true},{"patternId":"codesize_ExcessiveClassLength","enabled":true},{"patternId":"design_ImmutableField","enabled":true},{"patternId":"controversial_OneDeclarationPerLine","enabled":true},{"patternId":"empty_EmptyWhileStmt","enabled":true},{"patternId":"unnecessary_UnnecessaryReturn","enabled":true},{"patternId":"strings_InefficientEmptyStringCheck","enabled":true},{"patternId":"design_UseNotifyAllInsteadOfNotify","enabled":true},{"patternId":"strictexception_DoNotThrowExceptionInFinally","enabled":true},{"patternId":"junit_UseAssertEqualsInsteadOfAssertTrue","enabled":true},{"patternId":"typeresolution_CloneMethodMustImplementCloneable","enabled":true},{"patternId":"codesize_NPathComplexity","enabled":true},{"patternId":"imports_DontImportJavaLang","enabled":true},{"patternId":"empty_EmptySynchronizedBlock","enabled":true},{"patternId":"migrating_JUnit4TestShouldUseAfterAnnotation","enabled":true},{"patternId":"design_AvoidConstantsInterface","enabled":true},{"patternId":"unnecessary_UselessOperationOnImmutable","enabled":true},{"patternId":"design_PositionLiteralsFirstInComparisons","enabled":true},{"patternId":"migrating_ByteInstantiation","enabled":true},{"patternId":"junit_JUnitSpelling","enabled":true},{"patternId":"finalizers_EmptyFinalizer","enabled":true},{"patternId":"design_NonCaseLabelInSwitchStatement","enabled":true},{"patternId":"android_DoNotHardCodeSDCard","enabled":true},{"patternId":"design_LogicInversion","enabled":true},{"patternId":"unusedcode_UnusedPrivateMethod","enabled":true},{"patternId":"naming_AvoidDollarSigns","enabled":true},{"patternId":"finalizers_FinalizeShouldBeProtected","enabled":true},{"patternId":"clone_ProperCloneImplementation","enabled":true},{"patternId":"basic_CheckResultSet","enabled":true},{"patternId":"controversial_AvoidPrefixingMethodParameters","enabled":true},{"patternId":"migrating_JUnit4SuitesShouldUseSuiteAnnotation","enabled":true},{"patternId":"empty_EmptyIfStmt","enabled":true},{"patternId":"basic_DontCallThreadRun","enabled":true},{"patternId":"junit_JUnitStaticSuite","enabled":true},{"patternId":"optimizations_UseArraysAsList","enabled":true},{"patternId":"design_MissingStaticMethodInNonInstantiatableClass","enabled":true},{"patternId":"unusedcode_UnusedModifier","enabled":true},{"patternId":"Style_MethodName","enabled":true},{"patternId":"Metrics_CyclomaticComplexity","enabled":true},{"patternId":"Lint_DuplicateMethods","enabled":true},{"patternId":"Style_Lambda","enabled":true},{"patternId":"Lint_UselessSetterCall","enabled":true},{"patternId":"Style_VariableName","enabled":true},{"patternId":"Lint_AmbiguousOperator","enabled":true},{"patternId":"Style_LeadingCommentSpace","enabled":true},{"patternId":"Style_CaseEquality","enabled":true},{"patternId":"Lint_StringConversionInInterpolation","enabled":true},{"patternId":"Performance_ReverseEach","enabled":true},{"patternId":"Lint_LiteralInCondition","enabled":true},{"patternId":"Performance_Sample","enabled":true},{"patternId":"Style_NonNilCheck","enabled":true},{"patternId":"Lint_RescueException","enabled":true},{"patternId":"Lint_UselessElseWithoutRescue","enabled":true},{"patternId":"Style_ConstantName","enabled":true},{"patternId":"Lint_LiteralInInterpolation","enabled":true},{"patternId":"Lint_NestedMethodDefinition","enabled":true},{"patternId":"Style_DoubleNegation","enabled":true},{"patternId":"Lint_SpaceBeforeFirstArg","enabled":true},{"patternId":"Lint_Debugger","enabled":true},{"patternId":"Style_ClassVars","enabled":true},{"patternId":"Lint_EmptyEnsure","enabled":true},{"patternId":"Style_MultilineBlockLayout","enabled":true},{"patternId":"Lint_UnusedBlockArgument","enabled":true},{"patternId":"Lint_UselessAccessModifier","enabled":true},{"patternId":"Performance_Size","enabled":true},{"patternId":"Lint_EachWithObjectArgument","enabled":true},{"patternId":"Style_Alias","enabled":true},{"patternId":"Lint_Loop","enabled":true},{"patternId":"Style_NegatedWhile","enabled":true},{"patternId":"Style_ColonMethodCall","enabled":true},{"patternId":"Lint_AmbiguousRegexpLiteral","enabled":true},{"patternId":"Lint_UnusedMethodArgument","enabled":true},{"patternId":"Style_MultilineIfThen","enabled":true},{"patternId":"Lint_EnsureReturn","enabled":true},{"patternId":"Style_NegatedIf","enabled":true},{"patternId":"Lint_Eval","enabled":true},{"patternId":"Style_NilComparison","enabled":true},{"patternId":"Style_ArrayJoin","enabled":true},{"patternId":"Lint_ConditionPosition","enabled":true},{"patternId":"Lint_UnreachableCode","enabled":true},{"patternId":"Performance_Count","enabled":true},{"patternId":"Lint_EmptyInterpolation","enabled":true},{"patternId":"Style_LambdaCall","enabled":true},{"patternId":"Lint_HandleExceptions","enabled":true},{"patternId":"Lint_ShadowingOuterLocalVariable","enabled":true},{"patternId":"Lint_EndAlignment","enabled":true},{"patternId":"Style_MultilineTernaryOperator","enabled":true},{"patternId":"Style_AutoResourceCleanup","enabled":true},{"patternId":"Lint_ElseLayout","enabled":true},{"patternId":"Style_NestedTernaryOperator","enabled":true},{"patternId":"Style_OneLineConditional","enabled":true},{"patternId":"Style_EmptyElse","enabled":true},{"patternId":"Lint_UselessComparison","enabled":true},{"patternId":"Metrics_PerceivedComplexity","enabled":true},{"patternId":"Style_InfiniteLoop","enabled":true},{"patternId":"Rails_Date","enabled":true},{"patternId":"Style_EvenOdd","enabled":true},{"patternId":"Style_IndentationConsistency","enabled":true},{"patternId":"Style_ModuleFunction","enabled":true},{"patternId":"Lint_UselessAssignment","enabled":true},{"patternId":"Style_EachWithObject","enabled":true},{"patternId":"Performance_Detect","enabled":true},{"patternId":"duplicate_key","enabled":true},{"patternId":"no_interpolation_in_single_quotes","enabled":true},{"patternId":"no_backticks","enabled":true},{"patternId":"no_unnecessary_fat_arrows","enabled":true},{"patternId":"indentation","enabled":true},{"patternId":"ensure_comprehensions","enabled":true},{"patternId":"no_stand_alone_at","enabled":true},{"patternId":"cyclomatic_complexity","enabled":true},{"patternId":"Deserialize","enabled":true},{"patternId":"SymbolDoS","enabled":true},{"patternId":"SkipBeforeFilter","enabled":true},{"patternId":"SanitizeMethods","enabled":true},{"patternId":"SelectTag","enabled":true},{"patternId":"XMLDoS","enabled":true},{"patternId":"SimpleFormat","enabled":true},{"patternId":"Evaluation","enabled":true},{"patternId":"BasicAuth","enabled":true},{"patternId":"JRubyXML","enabled":true},{"patternId":"RenderInline","enabled":true},{"patternId":"YAMLParsing","enabled":true},{"patternId":"Redirect","enabled":true},{"patternId":"UnsafeReflection","enabled":true},{"patternId":"SSLVerify","enabled":true},{"patternId":"HeaderDoS","enabled":true},{"patternId":"TranslateBug","enabled":true},{"patternId":"Execute","enabled":true},{"patternId":"JSONParsing","enabled":true},{"patternId":"LinkTo","enabled":true},{"patternId":"FileDisclosure","enabled":true},{"patternId":"SafeBufferManipulation","enabled":true},{"patternId":"ModelAttributes","enabled":true},{"patternId":"ResponseSplitting","enabled":true},{"patternId":"DigestDoS","enabled":true},{"patternId":"Send","enabled":true},{"patternId":"MailTo","enabled":true},{"patternId":"SymbolDoSCVE","enabled":true},{"patternId":"StripTags","enabled":true},{"patternId":"MassAssignment","enabled":true},{"patternId":"RegexDoS","enabled":true},{"patternId":"SelectVulnerability","enabled":true},{"patternId":"FileAccess","enabled":true},{"patternId":"ContentTag","enabled":true},{"patternId":"SessionSettings","enabled":true},{"patternId":"FilterSkipping","enabled":true},{"patternId":"CreateWith","enabled":true},{"patternId":"JSONEncoding","enabled":true},{"patternId":"SQLCVEs","enabled":true},{"patternId":"ForgerySetting","enabled":true},{"patternId":"QuoteTableName","enabled":true},{"patternId":"I18nXSS","enabled":true},{"patternId":"WithoutProtection","enabled":true},{"patternId":"CrossSiteScripting","enabled":true},{"patternId":"SingleQuotes","enabled":true},{"patternId":"NestedAttributes","enabled":true},{"patternId":"DetailedExceptions","enabled":true},{"patternId":"LinkToHref","enabled":true},{"patternId":"RenderDoS","enabled":true},{"patternId":"ModelSerialize","enabled":true},{"patternId":"SQL","enabled":true},{"patternId":"Render","enabled":true},{"patternId":"UnscopedFind","enabled":true},{"patternId":"ValidationRegex","enabled":true},{"patternId":"EscapeFunction","enabled":true},{"patternId":"Custom_Scala_FieldNamesChecker","enabled":true},{"patternId":"Custom_Scala_ObjDeserialization","enabled":true},{"patternId":"Custom_Scala_RSAPadding","enabled":true},{"patternId":"ESLint_no-extra-boolean-cast","enabled":true},{"patternId":"ESLint_no-iterator","enabled":true},{"patternId":"ESLint_no-invalid-regexp","enabled":true},{"patternId":"ESLint_no-obj-calls","enabled":true},{"patternId":"ESLint_no-sparse-arrays","enabled":true},{"patternId":"ESLint_no-unreachable","enabled":true},{"patternId":"ESLint_no-dupe-keys","enabled":true},{"patternId":"ESLint_no-multi-str","enabled":true},{"patternId":"ESLint_no-extend-native","enabled":true},{"patternId":"ESLint_guard-for-in","enabled":true},{"patternId":"ESLint_no-func-assign","enabled":true},{"patternId":"ESLint_no-extra-semi","enabled":true},{"patternId":"ESLint_camelcase","enabled":true},{"patternId":"ESLint_no-mixed-spaces-and-tabs","enabled":true},{"patternId":"ESLint_no-undef","enabled":true},{"patternId":"ESLint_semi","enabled":true},{"patternId":"ESLint_no-empty-character-class","enabled":true},{"patternId":"ESLint_complexity","enabled":true},{"patternId":"ESLint_no-dupe-class-members","enabled":true},{"patternId":"ESLint_no-debugger","enabled":true},{"patternId":"ESLint_block-scoped-var","enabled":true},{"patternId":"ESLint_no-loop-func","enabled":true},{"patternId":"ESLint_no-use-before-define","enabled":true},{"patternId":"ESLint_no-console","enabled":true},{"patternId":"ESLint_require-yield","enabled":true},{"patternId":"ESLint_no-redeclare","enabled":true},{"patternId":"ESLint_no-undefined","enabled":true},{"patternId":"ESLint_use-isnan","enabled":true},{"patternId":"ESLint_no-control-regex","enabled":true},{"patternId":"ESLint_no-const-assign","enabled":true},{"patternId":"ESLint_no-new","enabled":true},{"patternId":"ESLint_new-cap","enabled":true},{"patternId":"ESLint_no-irregular-whitespace","enabled":true},{"patternId":"ESLint_object-shorthand","enabled":true},{"patternId":"ESLint_no-ex-assign","enabled":true},{"patternId":"ESLint_wrap-iife","enabled":true},{"patternId":"ESLint_arrow-parens","enabled":true},{"patternId":"ESLint_no-constant-condition","enabled":true},{"patternId":"ESLint_no-octal","enabled":true},{"patternId":"ESLint_no-dupe-args","enabled":true},{"patternId":"ESLint_quotes","enabled":true},{"patternId":"ESLint_no-fallthrough","enabled":true},{"patternId":"ESLint_no-delete-var","enabled":true},{"patternId":"ESLint_no-caller","enabled":true},{"patternId":"ESLint_no-cond-assign","enabled":true},{"patternId":"ESLint_no-this-before-super","enabled":true},{"patternId":"ESLint_no-negated-in-lhs","enabled":true},{"patternId":"ESLint_no-inner-declarations","enabled":true},{"patternId":"ESLint_eqeqeq","enabled":true},{"patternId":"ESLint_curly","enabled":true},{"patternId":"ESLint_arrow-spacing","enabled":true},{"patternId":"ESLint_no-empty","enabled":true},{"patternId":"ESLint_no-unused-vars","enabled":true},{"patternId":"ESLint_generator-star-spacing","enabled":true},{"patternId":"ESLint_no-duplicate-case","enabled":true},{"patternId":"ESLint_valid-typeof","enabled":true},{"patternId":"ESLint_no-regex-spaces","enabled":true},{"patternId":"ESLint_no-class-assign","enabled":true},{"patternId":"PyLint_W0221","enabled":true},{"patternId":"PyLint_E0117","enabled":true},{"patternId":"PyLint_E0001","enabled":true},{"patternId":"PyLint_E0241","enabled":true},{"patternId":"PyLint_W0404","enabled":true},{"patternId":"PyLint_E0704","enabled":true},{"patternId":"PyLint_E0703","enabled":true},{"patternId":"PyLint_E0302","enabled":true},{"patternId":"PyLint_W1301","enabled":true},{"patternId":"PyLint_R0201","enabled":true},{"patternId":"PyLint_E0113","enabled":true},{"patternId":"PyLint_W0410","enabled":true},{"patternId":"PyLint_C0123","enabled":true},{"patternId":"PyLint_E0115","enabled":true},{"patternId":"PyLint_E0114","enabled":true},{"patternId":"PyLint_E1126","enabled":true},{"patternId":"PyLint_W0702","enabled":true},{"patternId":"PyLint_W1303","enabled":true},{"patternId":"PyLint_W0622","enabled":true},{"patternId":"PyLint_W0222","enabled":true},{"patternId":"PyLint_W0233","enabled":true},{"patternId":"PyLint_W1305","enabled":true},{"patternId":"PyLint_E1127","enabled":true},{"patternId":"PyLint_E0112","enabled":true},{"patternId":"PyLint_W0611","enabled":true},{"patternId":"PyLint_W0601","enabled":true},{"patternId":"PyLint_W1300","enabled":true},{"patternId":"PyLint_W0124","enabled":true},{"patternId":"PyLint_R0203","enabled":true},{"patternId":"PyLint_E0236","enabled":true},{"patternId":"PyLint_W0612","enabled":true},{"patternId":"PyLint_W0604","enabled":true},{"patternId":"PyLint_W0705","enabled":true},{"patternId":"PyLint_E0238","enabled":true},{"patternId":"PyLint_W0602","enabled":true},{"patternId":"PyLint_R0102","enabled":true},{"patternId":"PyLint_R0202","enabled":true},{"patternId":"PyLint_E0240","enabled":true},{"patternId":"PyLint_W0623","enabled":true},{"patternId":"PyLint_W0711","enabled":true},{"patternId":"PyLint_E0116","enabled":true},{"patternId":"PyLint_E0239","enabled":true},{"patternId":"PyLint_E1132","enabled":true},{"patternId":"PyLint_W1307","enabled":true},{"patternId":"PyLint_C0200","enabled":true},{"patternId":"PyLint_E0301","enabled":true},{"patternId":"PyLint_W1306","enabled":true},{"patternId":"PyLint_W1302","enabled":true},{"patternId":"PyLint_E0110","enabled":true},{"patternId":"PyLint_E1125","enabled":true}]} \ No newline at end of file diff --git a/gcloud-java-bigquery/README.md b/gcloud-java-bigquery/README.md index 3387cd8c4f41..92fda2bad050 100644 --- a/gcloud-java-bigquery/README.md +++ b/gcloud-java-bigquery/README.md @@ -5,12 +5,12 @@ Java idiomatic client for [Google Cloud BigQuery] (https://cloud.google.com/bigq [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-bigquery.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-bigquery.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-bigquery.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-bigquery.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/bigquery/package-summary.html) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/bigquery/package-summary.html) > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. @@ -20,24 +20,24 @@ Quickstart If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-bigquery - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-bigquery:0.1.5' +compile 'com.google.cloud:gcloud-java-bigquery:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-bigquery" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-bigquery" % "0.2.0" ``` Example Application ------------------- -- [`BigQueryExample`](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java) - A simple command line interface providing some of Cloud BigQuery's functionality. -Read more about using this application on the [`BigQueryExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/bigquery/BigQueryExample.html). +- [`BigQueryExample`](../gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java) - A simple command line interface providing some of Cloud BigQuery's functionality. +Read more about using this application on the [`BigQueryExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/bigquery/BigQueryExample.html). Authentication -------------- @@ -82,8 +82,8 @@ These credentials are automatically inferred from your environment, so you only code to create your service object: ```java -import com.google.gcloud.bigquery.BigQuery; -import com.google.gcloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryOptions; BigQuery bigquery = BigQueryOptions.defaultInstance().service(); ``` @@ -96,7 +96,7 @@ With BigQuery you can create datasets. A dataset is a grouping mechanism that ho tables. Add the following import at the top of your file: ```java -import com.google.gcloud.bigquery.DatasetInfo; +import com.google.cloud.bigquery.DatasetInfo; ``` Then, to create the dataset, use the following code: @@ -113,12 +113,12 @@ are created from a BigQuery SQL query. In this code snippet we show how to creat with only one string field. Add the following imports at the top of your file: ```java -import com.google.gcloud.bigquery.Field; -import com.google.gcloud.bigquery.Schema; -import com.google.gcloud.bigquery.StandardTableDefinition; -import com.google.gcloud.bigquery.Table; -import com.google.gcloud.bigquery.TableId; -import com.google.gcloud.bigquery.TableInfo; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableId; +import com.google.cloud.bigquery.TableInfo; ``` Then add the following code to create the table: @@ -139,8 +139,8 @@ Google Cloud Storage file. In this code snippet we show how to stream rows into Add the following imports at the top of your file: ```java -import com.google.gcloud.bigquery.InsertAllRequest; -import com.google.gcloud.bigquery.InsertAllResponse; +import com.google.cloud.bigquery.InsertAllRequest; +import com.google.cloud.bigquery.InsertAllResponse; import java.util.HashMap; import java.util.Map; @@ -171,9 +171,9 @@ directly or through a Query Job. In this code snippet we show how to run a query for the result. Add the following imports at the top of your file: ```java -import com.google.gcloud.bigquery.FieldValue; -import com.google.gcloud.bigquery.QueryRequest; -import com.google.gcloud.bigquery.QueryResponse; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.QueryRequest; +import com.google.cloud.bigquery.QueryResponse; import java.util.Iterator; import java.util.List; @@ -185,7 +185,7 @@ Then add the following code to run the query and wait for the result: QueryRequest queryRequest = QueryRequest.builder("SELECT * FROM my_dataset_id.my_table_id") .maxWaitTime(60000L) - .maxResults(1000L) + .pageSize(1000L) .build(); // Request query to be executed and wait for results QueryResponse queryResponse = bigquery.query(queryRequest); @@ -203,7 +203,7 @@ while (rowIterator.hasNext()) { #### Complete source code In -[InsertDataAndQueryTable.java](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/InsertDataAndQueryTable.java) +[InsertDataAndQueryTable.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/InsertDataAndQueryTable.java) we put together all the code shown above into one program. The program assumes that you are running on Compute Engine or from your own desktop. To run the example on App Engine, simply move the code from the main method to your application's servlet class and change the print statements to @@ -255,4 +255,4 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-bigquery]: https://cloud.google.com/bigquery/ [cloud-storage]: https://cloud.google.com/storage/ -[bigquery-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/bigquery/package-summary.html +[bigquery-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/bigquery/package-summary.html diff --git a/gcloud-java-bigquery/pom.xml b/gcloud-java-bigquery/pom.xml index 9a2137cb987d..4ad6a43b6be3 100644 --- a/gcloud-java-bigquery/pom.xml +++ b/gcloud-java-bigquery/pom.xml @@ -4,13 +4,14 @@ gcloud-java-bigquery jar GCloud Java bigquery + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-bigquery Java idiomatic client for Google Cloud BigQuery. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-bigquery @@ -39,6 +40,13 @@ + + ${project.groupId} + gcloud-java-core + ${project.version} + test-jar + test + junit junit diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Acl.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java similarity index 99% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Acl.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java index b8e1a817c836..376b090973a5 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Acl.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQuery.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java similarity index 83% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQuery.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java index 3acaacaf42e5..90ed343e0e34 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQuery.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java @@ -14,21 +14,20 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkArgument; +import com.google.cloud.FieldSelector; +import com.google.cloud.FieldSelector.Helper; +import com.google.cloud.Page; +import com.google.cloud.Service; +import com.google.cloud.bigquery.spi.BigQueryRpc; import com.google.common.base.Function; -import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.gcloud.Page; -import com.google.gcloud.Service; -import com.google.gcloud.bigquery.spi.BigQueryRpc; import java.util.List; -import java.util.Set; /** * An interface for Google Cloud BigQuery. @@ -43,7 +42,7 @@ public interface BigQuery extends Service { * @see Dataset * Resource */ - enum DatasetField { + enum DatasetField implements FieldSelector { ACCESS("access"), CREATION_TIME("creationTime"), DATASET_REFERENCE("datasetReference"), @@ -56,24 +55,19 @@ enum DatasetField { LOCATION("location"), SELF_LINK("selfLink"); + static final List REQUIRED_FIELDS = + ImmutableList.of(DATASET_REFERENCE); + private final String selector; DatasetField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(DatasetField... fields) { - Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); - fieldStrings.add(DATASET_REFERENCE.selector()); - for (DatasetField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } /** @@ -82,7 +76,7 @@ static String selector(DatasetField... fields) { * @see Table * Resource */ - enum TableField { + enum TableField implements FieldSelector { CREATION_TIME("creationTime"), DESCRIPTION("description"), ETAG("etag"), @@ -101,25 +95,19 @@ enum TableField { TYPE("type"), VIEW("view"); + static final List REQUIRED_FIELDS = + ImmutableList.of(TABLE_REFERENCE, TYPE); + private final String selector; TableField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(TableField... fields) { - Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 2); - fieldStrings.add(TABLE_REFERENCE.selector()); - fieldStrings.add(TYPE.selector()); - for (TableField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } /** @@ -128,7 +116,7 @@ static String selector(TableField... fields) { * @see Job Resource * */ - enum JobField { + enum JobField implements FieldSelector { CONFIGURATION("configuration"), ETAG("etag"), ID("id"), @@ -138,25 +126,19 @@ enum JobField { STATUS("status"), USER_EMAIL("user_email"); + static final List REQUIRED_FIELDS = + ImmutableList.of(JOB_REFERENCE, CONFIGURATION); + private final String selector; JobField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(JobField... fields) { - Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 2); - fieldStrings.add(JOB_REFERENCE.selector()); - fieldStrings.add(CONFIGURATION.selector()); - for (JobField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } /** @@ -171,16 +153,16 @@ private DatasetListOption(BigQueryRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of datasets to be returned. + * Returns an option to specify the maximum number of datasets returned per page. */ - public static DatasetListOption maxResults(long maxResults) { - return new DatasetListOption(BigQueryRpc.Option.MAX_RESULTS, maxResults); + public static DatasetListOption pageSize(long pageSize) { + return new DatasetListOption(BigQueryRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing datasets. */ - public static DatasetListOption startPageToken(String pageToken) { + public static DatasetListOption pageToken(String pageToken) { return new DatasetListOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken); } @@ -210,7 +192,8 @@ private DatasetOption(BigQueryRpc.Option option, Object value) { * returned, even if not specified. */ public static DatasetOption fields(DatasetField... fields) { - return new DatasetOption(BigQueryRpc.Option.FIELDS, DatasetField.selector(fields)); + return new DatasetOption(BigQueryRpc.Option.FIELDS, + Helper.selector(DatasetField.REQUIRED_FIELDS, fields)); } } @@ -246,17 +229,17 @@ private TableListOption(BigQueryRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of tables to be returned. + * Returns an option to specify the maximum number of tables returned per page. */ - public static TableListOption maxResults(long maxResults) { - checkArgument(maxResults >= 0); - return new TableListOption(BigQueryRpc.Option.MAX_RESULTS, maxResults); + public static TableListOption pageSize(long pageSize) { + checkArgument(pageSize >= 0); + return new TableListOption(BigQueryRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing tables. */ - public static TableListOption startPageToken(String pageToken) { + public static TableListOption pageToken(String pageToken) { return new TableListOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken); } } @@ -279,7 +262,8 @@ private TableOption(BigQueryRpc.Option option, Object value) { * of {@link Table#definition()}) are always returned, even if not specified. */ public static TableOption fields(TableField... fields) { - return new TableOption(BigQueryRpc.Option.FIELDS, TableField.selector(fields)); + return new TableOption(BigQueryRpc.Option.FIELDS, + Helper.selector(TableField.REQUIRED_FIELDS, fields)); } } @@ -295,17 +279,17 @@ private TableDataListOption(BigQueryRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of rows to be returned. + * Returns an option to specify the maximum number of rows returned per page. */ - public static TableDataListOption maxResults(long maxResults) { - checkArgument(maxResults >= 0); - return new TableDataListOption(BigQueryRpc.Option.MAX_RESULTS, maxResults); + public static TableDataListOption pageSize(long pageSize) { + checkArgument(pageSize >= 0); + return new TableDataListOption(BigQueryRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing table data. */ - public static TableDataListOption startPageToken(String pageToken) { + public static TableDataListOption pageToken(String pageToken) { return new TableDataListOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken); } @@ -352,17 +336,17 @@ public String apply(JobStatus.State state) { } /** - * Returns an option to specify the maximum number of jobs to be returned. + * Returns an option to specify the maximum number of jobs returned per page. */ - public static JobListOption maxResults(long maxResults) { - checkArgument(maxResults >= 0); - return new JobListOption(BigQueryRpc.Option.MAX_RESULTS, maxResults); + public static JobListOption pageSize(long pageSize) { + checkArgument(pageSize >= 0); + return new JobListOption(BigQueryRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing jobs. */ - public static JobListOption startPageToken(String pageToken) { + public static JobListOption pageToken(String pageToken) { return new JobListOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken); } @@ -376,10 +360,8 @@ public static JobListOption startPageToken(String pageToken) { * listing jobs. */ public static JobListOption fields(JobField... fields) { - String selector = JobField.selector(fields); - StringBuilder builder = new StringBuilder(); - builder.append("etag,jobs(").append(selector).append(",state,errorResult),nextPageToken"); - return new JobListOption(BigQueryRpc.Option.FIELDS, builder.toString()); + return new JobListOption(BigQueryRpc.Option.FIELDS, + Helper.listSelector("jobs", JobField.REQUIRED_FIELDS, fields, "state", "errorResult")); } } @@ -402,7 +384,8 @@ private JobOption(BigQueryRpc.Option option, Object value) { * returned, even if not specified. */ public static JobOption fields(JobField... fields) { - return new JobOption(BigQueryRpc.Option.FIELDS, JobField.selector(fields)); + return new JobOption(BigQueryRpc.Option.FIELDS, + Helper.selector(JobField.REQUIRED_FIELDS, fields)); } } @@ -418,17 +401,17 @@ private QueryResultsOption(BigQueryRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of rows to be returned. + * Returns an option to specify the maximum number of rows returned per page. */ - public static QueryResultsOption maxResults(long maxResults) { - checkArgument(maxResults >= 0); - return new QueryResultsOption(BigQueryRpc.Option.MAX_RESULTS, maxResults); + public static QueryResultsOption pageSize(long pageSize) { + checkArgument(pageSize >= 0); + return new QueryResultsOption(BigQueryRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start getting query results. */ - public static QueryResultsOption startPageToken(String pageToken) { + public static QueryResultsOption pageToken(String pageToken) { return new QueryResultsOption(BigQueryRpc.Option.PAGE_TOKEN, pageToken); } @@ -488,9 +471,10 @@ public static QueryResultsOption maxWaitTime(long maxWaitTime) { Dataset getDataset(DatasetId datasetId, DatasetOption... options); /** - * Lists the project's datasets. This method returns partial information on each dataset - * ({@link Dataset#datasetId()}, {@link Dataset#friendlyName()} and {@link Dataset#id()}). To get - * complete information use either {@link #getDataset(String, DatasetOption...)} or + * Lists the project's datasets. This method returns partial information on each dataset: + * ({@link Dataset#datasetId()}, {@link Dataset#friendlyName()} and + * {@link Dataset#generatedId()}). To get complete information use either + * {@link #getDataset(String, DatasetOption...)} or * {@link #getDataset(DatasetId, DatasetOption...)}. * * @throws BigQueryException upon failure @@ -558,9 +542,9 @@ public static QueryResultsOption maxWaitTime(long maxWaitTime) { Table getTable(TableId tableId, TableOption... options); /** - * Lists the tables in the dataset. This method returns partial information on each table - * ({@link Table#tableId()}, {@link Table#friendlyName()}, {@link Table#id()} and type, which - * is part of {@link Table#definition()}). To get complete information use either + * Lists the tables in the dataset. This method returns partial information on each table: + * ({@link Table#tableId()}, {@link Table#friendlyName()}, {@link Table#generatedId()} and type, + * which is part of {@link Table#definition()}). To get complete information use either * {@link #getTable(TableId, TableOption...)} or * {@link #getTable(String, String, TableOption...)}. * @@ -569,9 +553,9 @@ public static QueryResultsOption maxWaitTime(long maxWaitTime) { Page listTables(String datasetId, TableListOption... options); /** - * Lists the tables in the dataset. This method returns partial information on each table - * ({@link Table#tableId()}, {@link Table#friendlyName()}, {@link Table#id()} and type, which - * is part of {@link Table#definition()}). To get complete information use either + * Lists the tables in the dataset. This method returns partial information on each table: + * ({@link Table#tableId()}, {@link Table#friendlyName()}, {@link Table#generatedId()} and type, + * which is part of {@link Table#definition()}). To get complete information use either * {@link #getTable(TableId, TableOption...)} or * {@link #getTable(String, String, TableOption...)}. * diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryError.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryError.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java index e58f0d0b7213..121ca578c0d3 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryError.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryError.java @@ -1,4 +1,4 @@ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.ErrorProto; import com.google.common.base.Function; @@ -17,7 +17,7 @@ * {@link BigQueryException} is thrown the BigQuery Error that caused it, if any, can be accessed * with {@link BigQueryException#error()}. */ -public class BigQueryError implements Serializable { +public final class BigQueryError implements Serializable { static final Function FROM_PB_FUNCTION = new Function() { @@ -98,7 +98,9 @@ public String toString() { @Override public boolean equals(Object obj) { - return obj instanceof BigQueryError && Objects.equals(toPb(), ((BigQueryError) obj).toPb()); + return obj == this + || obj instanceof BigQueryError + && Objects.equals(toPb(), ((BigQueryError) obj).toPb()); } ErrorProto toPb() { diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryException.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryException.java similarity index 80% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryException.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryException.java index a157afd25db2..7f314a01b088 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryException.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryException.java @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryHelper.RetryInterruptedException; import java.io.IOException; +import java.util.Objects; import java.util.Set; /** @@ -73,6 +74,23 @@ protected Set retryableErrors() { return RETRYABLE_ERRORS; } + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof BigQueryException)) { + return false; + } + BigQueryException other = (BigQueryException) obj; + return super.equals(other) && Objects.equals(error, other.error); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), error); + } + /** * Translate RetryHelperException to the BigQueryException that caused the error. This method will * always throw an exception. diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryFactory.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryFactory.java similarity index 90% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryFactory.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryFactory.java index 90e7bbccd483..064a7c9ba46e 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryFactory.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryFactory.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; -import com.google.gcloud.ServiceFactory; +import com.google.cloud.ServiceFactory; /** * An interface for BigQuery factories. diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryImpl.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryImpl.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java index 27f4af5d5007..62a72647beff 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryImpl.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java @@ -14,28 +14,28 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; +import static com.google.cloud.RetryHelper.runWithRetries; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.gcloud.RetryHelper.runWithRetries; import com.google.api.services.bigquery.model.GetQueryResultsResponse; import com.google.api.services.bigquery.model.TableDataInsertAllRequest; import com.google.api.services.bigquery.model.TableDataInsertAllRequest.Rows; import com.google.api.services.bigquery.model.TableRow; +import com.google.cloud.BaseService; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.PageImpl.NextPageFetcher; +import com.google.cloud.RetryHelper; +import com.google.cloud.bigquery.InsertAllRequest.RowToInsert; +import com.google.cloud.bigquery.spi.BigQueryRpc; 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.Lists; import com.google.common.collect.Maps; -import com.google.gcloud.BaseService; -import com.google.gcloud.Page; -import com.google.gcloud.PageImpl; -import com.google.gcloud.PageImpl.NextPageFetcher; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.bigquery.InsertAllRequest.RowToInsert; -import com.google.gcloud.bigquery.spi.BigQueryRpc; import java.util.List; import java.util.Map; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryOptions.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java similarity index 91% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryOptions.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java index d48cf646f349..1845755bae67 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/BigQueryOptions.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryOptions.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; +import com.google.cloud.ServiceOptions; +import com.google.cloud.bigquery.spi.BigQueryRpc; +import com.google.cloud.bigquery.spi.BigQueryRpcFactory; +import com.google.cloud.bigquery.spi.DefaultBigQueryRpc; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.bigquery.spi.BigQueryRpc; -import com.google.gcloud.bigquery.spi.BigQueryRpcFactory; -import com.google.gcloud.bigquery.spi.DefaultBigQueryRpc; import java.util.Set; @@ -85,6 +85,7 @@ protected Set scopes() { return SCOPES; } + @SuppressWarnings("unchecked") @Override public Builder toBuilder() { return new Builder(this); diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CopyJobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CopyJobConfiguration.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CopyJobConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CopyJobConfiguration.java index c12cbf5fe432..e455416bea7b 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CopyJobConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CopyJobConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -181,7 +181,9 @@ ToStringHelper toStringHelper() { @Override public boolean equals(Object obj) { - return obj instanceof CopyJobConfiguration && baseEquals((CopyJobConfiguration) obj); + return obj == this + || obj instanceof CopyJobConfiguration + && baseEquals((CopyJobConfiguration) obj); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CsvOptions.java similarity index 97% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CsvOptions.java index 9576e7d75640..b621ed2cc6bc 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/CsvOptions.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/CsvOptions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.common.base.MoreObjects; @@ -25,7 +25,7 @@ * Google BigQuery options for CSV format. This class wraps some properties of CSV files used by * BigQuery to parse external data. */ -public class CsvOptions extends FormatOptions { +public final class CsvOptions extends FormatOptions { private static final long serialVersionUID = 2193570529308612708L; @@ -224,7 +224,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof CsvOptions && Objects.equals(toPb(), ((CsvOptions) obj).toPb()); + return obj == this + || obj instanceof CsvOptions + && Objects.equals(toPb(), ((CsvOptions) obj).toPb()); } com.google.api.services.bigquery.model.CsvOptions toPb() { diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Dataset.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java similarity index 92% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Dataset.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java index e17d3e82c4ef..5cc7a260f00f 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Dataset.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Dataset.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.gcloud.Page; +import com.google.cloud.Page; import java.io.IOException; import java.io.ObjectInputStream; @@ -34,7 +34,7 @@ * {@link DatasetInfo}. *

*/ -public final class Dataset extends DatasetInfo { +public class Dataset extends DatasetInfo { private static final long serialVersionUID = -4272921483363065593L; @@ -103,8 +103,8 @@ public Builder friendlyName(String friendlyName) { } @Override - Builder id(String id) { - infoBuilder.id(id); + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); return this; } @@ -230,14 +230,20 @@ public Builder toBuilder() { } @Override - public boolean equals(Object obj) { - return obj instanceof Dataset - && Objects.equals(toPb(), ((Dataset) obj).toPb()) - && Objects.equals(options, ((Dataset) obj).options); + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Dataset.class)) { + return false; + } + Dataset other = (Dataset) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), options); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetId.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetId.java similarity index 91% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetId.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetId.java index 006c089f8d63..a6f2762da9cd 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetId.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetId.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -26,7 +26,7 @@ /** * Google BigQuery Dataset identity. */ -public class DatasetId implements Serializable { +public final class DatasetId implements Serializable { private static final long serialVersionUID = -6186254820908152300L; @@ -68,7 +68,9 @@ public static DatasetId of(String dataset) { @Override public boolean equals(Object obj) { - return obj instanceof DatasetId && Objects.equals(toPb(), ((DatasetId) obj).toPb()); + return obj == this + || obj instanceof DatasetId + && Objects.equals(toPb(), ((DatasetId) obj).toPb()); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetInfo.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetInfo.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java index aa767b97631b..3e7da7cc6c87 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/DatasetInfo.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/DatasetInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; @@ -55,8 +55,7 @@ public Dataset apply(DatasetInfo datasetInfo) { return datasetInfo.toPb(); } }; - - private static final long serialVersionUID = -6615133444520365839L; + private static final long serialVersionUID = 8469473744160758489L; private final DatasetId datasetId; private final List acl; @@ -65,7 +64,7 @@ public Dataset apply(DatasetInfo datasetInfo) { private final String description; private final String etag; private final String friendlyName; - private final String id; + private final String generatedId; private final Long lastModified; private final String location; private final String selfLink; @@ -114,7 +113,7 @@ public abstract static class Builder { */ public abstract Builder friendlyName(String friendlyName); - abstract Builder id(String id); + abstract Builder generatedId(String generatedId); abstract Builder lastModified(Long lastModified); @@ -144,7 +143,7 @@ static final class BuilderImpl extends Builder { private String description; private String etag; private String friendlyName; - private String id; + private String generatedId; private Long lastModified; private String location; private String selfLink; @@ -159,7 +158,7 @@ static final class BuilderImpl extends Builder { this.description = datasetInfo.description; this.etag = datasetInfo.etag; this.friendlyName = datasetInfo.friendlyName; - this.id = datasetInfo.id; + this.generatedId = datasetInfo.generatedId; this.lastModified = datasetInfo.lastModified; this.location = datasetInfo.location; this.selfLink = datasetInfo.selfLink; @@ -182,7 +181,7 @@ public Acl apply(Dataset.Access accessPb) { this.description = datasetPb.getDescription(); this.etag = datasetPb.getEtag(); this.friendlyName = datasetPb.getFriendlyName(); - this.id = datasetPb.getId(); + this.generatedId = datasetPb.getId(); this.lastModified = datasetPb.getLastModifiedTime(); this.location = datasetPb.getLocation(); this.selfLink = datasetPb.getSelfLink(); @@ -232,8 +231,8 @@ public Builder friendlyName(String friendlyName) { } @Override - Builder id(String id) { - this.id = id; + Builder generatedId(String generatedId) { + this.generatedId = generatedId; return this; } @@ -269,7 +268,7 @@ public DatasetInfo build() { description = builder.description; etag = builder.etag; friendlyName = builder.friendlyName; - id = builder.id; + generatedId = builder.generatedId; lastModified = builder.lastModified; location = builder.location; selfLink = builder.selfLink; @@ -333,10 +332,10 @@ public String friendlyName() { } /** - * Returns an opaque id for the dataset. + * Returns the service-generated id for the dataset. */ - public String id() { - return id; + public String generatedId() { + return generatedId; } /** @@ -381,7 +380,7 @@ public String toString() { .add("description", description) .add("etag", etag) .add("friendlyName", friendlyName) - .add("id", id) + .add("generatedId", generatedId) .add("lastModified", lastModified) .add("location", location) .add("selfLink", selfLink) @@ -396,7 +395,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj != null + return obj == this + || obj != null && obj.getClass().equals(DatasetInfo.class) && Objects.equals(toPb(), ((DatasetInfo) obj).toPb()); } @@ -431,7 +431,7 @@ Dataset toPb() { datasetPb.setDescription(description); datasetPb.setEtag(etag); datasetPb.setFriendlyName(friendlyName); - datasetPb.setId(id); + datasetPb.setId(generatedId); datasetPb.setLastModifiedTime(lastModified); datasetPb.setLocation(location); datasetPb.setSelfLink(selfLink); diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableDefinition.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExternalTableDefinition.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableDefinition.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExternalTableDefinition.java index 5f396d948f5a..af17c281f7f1 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExternalTableDefinition.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExternalTableDefinition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -255,12 +255,15 @@ ToStringHelper toStringHelper() { } @Override - public boolean equals(Object obj) { - return obj instanceof ExternalTableDefinition && baseEquals((ExternalTableDefinition) obj); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ExternalTableDefinition.class) + && baseEquals((ExternalTableDefinition) obj); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(baseHashCode(), compression, ignoreUnknownValues, maxBadRecords, formatOptions, sourceUris); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExtractJobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExtractJobConfiguration.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExtractJobConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExtractJobConfiguration.java index 7c5a2698b159..3dccddcae764 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ExtractJobConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ExtractJobConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -211,7 +211,9 @@ ToStringHelper toStringHelper() { @Override public boolean equals(Object obj) { - return obj instanceof ExtractJobConfiguration && baseEquals((ExtractJobConfiguration) obj); + return obj == this + || obj instanceof ExtractJobConfiguration + && baseEquals((ExtractJobConfiguration) obj); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Field.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Field.java index 55fae44c5eed..dc805e12c2a2 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Field.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Field.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; @@ -32,12 +32,12 @@ import java.util.Objects; /** - * Google BigQuery Table field. A table field has a name, a value, a mode and possibly a - * description. Supported types are: {@link Type#integer()}, {@link Type#bool()}, - * {@link Type#string()}, {@link Type#floatingPoint()}, {@link Type#timestamp()} and - * {@link Type#record(Field...)}. One or more fields form a table's schema. + * Google BigQuery Table field. A table field has a name, a type, a mode and possibly a description. + * Supported types are: {@link Type#integer()}, {@link Type#bool()}, {@link Type#string()}, + * {@link Type#floatingPoint()}, {@link Type#timestamp()} and {@link Type#record(Field...)}. One or + * more fields form a table's schema. */ -public class Field implements Serializable { +public final class Field implements Serializable { static final Function FROM_PB_FUNCTION = new Function() { @@ -56,6 +56,11 @@ public TableFieldSchema apply(Field field) { private static final long serialVersionUID = -8154262932305199256L; + private final String name; + private final Type type; + private final String mode; + private final String description; + /** * Data Types for a BigQuery Table field. This class provides factory methods for all BigQuery * field types. To instantiate a RECORD value the list of sub-fields must be provided. @@ -185,11 +190,6 @@ public enum Mode { NULLABLE, REQUIRED, REPEATED } - private final String name; - private final Type type; - private final String mode; - private final String description; - public static final class Builder { private String name; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FieldValue.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java similarity index 97% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FieldValue.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java index 8b27c70db782..1c06b87d639d 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FieldValue.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -216,13 +216,16 @@ public String toString() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(attribute, value); } @Override - public boolean equals(Object obj) { - if (!(obj instanceof FieldValue)) { + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(FieldValue.class)) { return false; } FieldValue other = (FieldValue) obj; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java similarity index 92% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java index f46e7b40f4c1..4267d5384147 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/FormatOptions.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/FormatOptions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -59,7 +59,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof FormatOptions && Objects.equals(type, ((FormatOptions) obj).type()); + return obj == this + || obj != null + && obj.getClass().equals(FormatOptions.class) + && Objects.equals(type, ((FormatOptions) obj).type()); } /** diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllRequest.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllRequest.java similarity index 99% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllRequest.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllRequest.java index f0d61583f83f..b46257833b78 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllRequest.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -37,7 +37,7 @@ * @see Streaming Data into * BigQuery */ -public class InsertAllRequest implements Serializable { +public final class InsertAllRequest implements Serializable { private static final long serialVersionUID = 211200307773853078L; @@ -443,6 +443,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } if (!(obj instanceof InsertAllRequest)) { return false; } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllResponse.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllResponse.java index 992c5d851bbc..a145d1037bf7 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/InsertAllResponse.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/InsertAllResponse.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.ErrorProto; import com.google.api.services.bigquery.model.TableDataInsertAllResponse; @@ -74,13 +74,15 @@ public boolean hasErrors() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(insertErrors); } @Override - public boolean equals(Object obj) { - return obj instanceof InsertAllResponse + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(InsertAllResponse.class) && Objects.equals(insertErrors, ((InsertAllResponse) obj).insertErrors); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java similarity index 85% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index 1e63344a600d..bfcca5b5388a 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Job.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -30,7 +30,7 @@ * {@link JobInfo}. *

*/ -public final class Job extends JobInfo { +public class Job extends JobInfo { private static final long serialVersionUID = -4324100991693024704L; @@ -63,8 +63,8 @@ Builder etag(String etag) { } @Override - Builder id(String id) { - infoBuilder.id(id); + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); return this; } @@ -128,22 +128,19 @@ public boolean exists() { /** * Checks if this job has completed its execution, either failing or succeeding. If the job does - * not exist this method returns {@code false}. To correctly wait for job's completion check that - * the job exists first, using {@link #exists()}: + * not exist this method returns {@code true}. You can wait for job completion with: *
 {@code
-   * if (job.exists()) {
-   *   while(!job.isDone()) {
-   *     Thread.sleep(1000L);
-   *   }
+   * while(!job.isDone()) {
+   *   Thread.sleep(1000L);
    * }}
* - * @return {@code true} if this job is in {@link JobStatus.State#DONE} state, {@code false} if the - * state is not {@link JobStatus.State#DONE} or the job does not exist + * @return {@code true} if this job is in {@link JobStatus.State#DONE} state or if it does not + * exist, {@code false} if the state is not {@link JobStatus.State#DONE} * @throws BigQueryException upon failure */ public boolean isDone() { Job job = bigquery.getJob(jobId(), BigQuery.JobOption.fields(BigQuery.JobField.STATUS)); - return job != null && job.status().state() == JobStatus.State.DONE; + return job == null || job.status().state() == JobStatus.State.DONE; } /** @@ -181,14 +178,20 @@ public Builder toBuilder() { } @Override - public boolean equals(Object obj) { - return obj instanceof Job - && Objects.equals(toPb(), ((Job) obj).toPb()) - && Objects.equals(options, ((Job) obj).options); + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Job.class)) { + return false; + } + Job other = (Job) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), options); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobConfiguration.java similarity index 99% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobConfiguration.java index 2244969567ef..85653e357a96 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobId.java similarity index 91% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobId.java index 898c894f9a21..d2981151481f 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobId.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobId.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -26,7 +26,7 @@ /** * Google BigQuery Job identity. */ -public class JobId implements Serializable { +public final class JobId implements Serializable { private static final long serialVersionUID = 1225914835379688976L; @@ -68,7 +68,9 @@ public static JobId of(String job) { @Override public boolean equals(Object obj) { - return obj instanceof JobId && Objects.equals(toPb(), ((JobId) obj).toPb()); + return obj == this + || obj instanceof JobId + && Objects.equals(toPb(), ((JobId) obj).toPb()); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobInfo.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobInfo.java index 1adf7fabafc1..13addc9d6e7a 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobInfo.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.Job; import com.google.common.base.Function; @@ -42,10 +42,10 @@ public JobInfo apply(Job pb) { } }; - private static final long serialVersionUID = -3272941007234620265L; + private static final long serialVersionUID = 2740548743267670124L; private final String etag; - private final String id; + private final String generatedId; private final JobId jobId; private final String selfLink; private final JobStatus status; @@ -95,7 +95,7 @@ public abstract static class Builder { abstract Builder etag(String etag); - abstract Builder id(String id); + abstract Builder generatedId(String generatedId); /** * Sets the job identity. @@ -128,7 +128,7 @@ public abstract static class Builder { static final class BuilderImpl extends Builder { private String etag; - private String id; + private String generatedId; private JobId jobId; private String selfLink; private JobStatus status; @@ -140,7 +140,7 @@ static final class BuilderImpl extends Builder { BuilderImpl(JobInfo jobInfo) { this.etag = jobInfo.etag; - this.id = jobInfo.id; + this.generatedId = jobInfo.generatedId; this.jobId = jobInfo.jobId; this.selfLink = jobInfo.selfLink; this.status = jobInfo.status; @@ -151,7 +151,7 @@ static final class BuilderImpl extends Builder { BuilderImpl(Job jobPb) { this.etag = jobPb.getEtag(); - this.id = jobPb.getId(); + this.generatedId = jobPb.getId(); if (jobPb.getJobReference() != null) { this.jobId = JobId.fromPb(jobPb.getJobReference()); } @@ -173,8 +173,8 @@ Builder etag(String etag) { } @Override - Builder id(String id) { - this.id = id; + Builder generatedId(String generatedId) { + this.generatedId = generatedId; return this; } @@ -223,7 +223,7 @@ public JobInfo build() { JobInfo(BuilderImpl builder) { this.jobId = builder.jobId; this.etag = builder.etag; - this.id = builder.id; + this.generatedId = builder.generatedId; this.selfLink = builder.selfLink; this.status = builder.status; this.statistics = builder.statistics; @@ -239,10 +239,10 @@ public String etag() { } /** - * Returns an opaque id for the job. + * Returns the service-generated id for the job. */ - public String id() { - return id; + public String generatedId() { + return generatedId; } /** @@ -306,7 +306,7 @@ public String toString() { .add("statistics", statistics) .add("userEmail", userEmail) .add("etag", etag) - .add("id", id) + .add("generatedId", generatedId) .add("selfLink", selfLink) .add("configuration", configuration) .toString(); @@ -319,7 +319,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj != null + return obj == this + || obj != null && obj.getClass().equals(JobInfo.class) && Objects.equals(toPb(), ((JobInfo) obj).toPb()); } @@ -331,7 +332,7 @@ JobInfo setProjectId(String projectId) { Job toPb() { Job jobPb = new Job(); jobPb.setEtag(etag); - jobPb.setId(id); + jobPb.setId(generatedId); jobPb.setSelfLink(selfLink); jobPb.setUserEmail(userEmail); if (jobId != null) { diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java similarity index 84% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java index 34e4917921ba..90ad164a7ce5 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatistics.java @@ -1,4 +1,4 @@ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.JobStatistics2; import com.google.api.services.bigquery.model.JobStatistics3; @@ -14,7 +14,7 @@ /** * A Google BigQuery Job statistics. */ -public class JobStatistics implements Serializable { +public abstract class JobStatistics implements Serializable { private static final long serialVersionUID = 1433024714741660399L; @@ -22,6 +22,55 @@ public class JobStatistics implements Serializable { private final Long endTime; private final Long startTime; + /** + * A Google BigQuery Copy Job statistics. + */ + public static class CopyStatistics extends JobStatistics { + + private static final long serialVersionUID = 8218325588441660938L; + + static final class Builder extends JobStatistics.Builder { + + private Builder() {} + + private Builder(com.google.api.services.bigquery.model.JobStatistics statisticsPb) { + super(statisticsPb); + } + + @Override + CopyStatistics build() { + return new CopyStatistics(this); + } + } + + private CopyStatistics(Builder builder) { + super(builder); + } + + @Override + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(CopyStatistics.class) + && baseEquals((CopyStatistics) obj); + } + + @Override + public final int hashCode() { + return baseHashCode(); + } + + static Builder builder() { + return new Builder(); + } + + @SuppressWarnings("unchecked") + static CopyStatistics fromPb( + com.google.api.services.bigquery.model.JobStatistics statisticPb) { + return new Builder(statisticPb).build(); + } + } + /** * A Google BigQuery Extract Job statistics. */ @@ -73,14 +122,16 @@ ToStringHelper toStringHelper() { } @Override - public boolean equals(Object obj) { - return obj instanceof ExtractStatistics - && Objects.equals(toPb(), ((ExtractStatistics) obj).toPb()); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ExtractStatistics.class) + && baseEquals((ExtractStatistics) obj); } @Override - public int hashCode() { - return Objects.hash(super.hashCode(), destinationUriFileCounts); + public final int hashCode() { + return Objects.hash(baseHashCode(), destinationUriFileCounts); } @Override @@ -203,13 +254,16 @@ ToStringHelper toStringHelper() { } @Override - public boolean equals(Object obj) { - return obj instanceof LoadStatistics && Objects.equals(toPb(), ((LoadStatistics) obj).toPb()); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(LoadStatistics.class) + && baseEquals((LoadStatistics) obj); } @Override - public int hashCode() { - return Objects.hash(super.hashCode(), inputBytes, inputFiles, outputBytes, outputRows); + public final int hashCode() { + return Objects.hash(baseHashCode(), inputBytes, inputFiles, outputBytes, outputRows); } @Override @@ -361,14 +415,16 @@ ToStringHelper toStringHelper() { } @Override - public boolean equals(Object obj) { - return obj instanceof QueryStatistics - && Objects.equals(toPb(), ((QueryStatistics) obj).toPb()); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(QueryStatistics.class) + && baseEquals((QueryStatistics) obj); } @Override - public int hashCode() { - return Objects.hash(super.hashCode(), billingTier, cacheHit, totalBytesBilled, + public final int hashCode() { + return Objects.hash(baseHashCode(), billingTier, cacheHit, totalBytesBilled, totalBytesProcessed, queryPlan); } @@ -396,7 +452,7 @@ static QueryStatistics fromPb( } } - static class Builder> { + abstract static class Builder> { private Long creationTime; private Long endTime; @@ -430,10 +486,7 @@ B startTime(Long startTime) { return self(); } - @SuppressWarnings("unchecked") - T build() { - return (T) new JobStatistics(this); - } + abstract T build(); } protected JobStatistics(Builder builder) { @@ -477,14 +530,12 @@ public String toString() { return toStringHelper().toString(); } - @Override - public int hashCode() { + final int baseHashCode() { return Objects.hash(creationTime, endTime, startTime); } - @Override - public boolean equals(Object obj) { - return obj instanceof JobStatistics && Objects.equals(toPb(), ((JobStatistics) obj).toPb()); + final boolean baseEquals(JobStatistics jobStatistics) { + return Objects.equals(toPb(), jobStatistics.toPb()); } com.google.api.services.bigquery.model.JobStatistics toPb() { @@ -496,10 +547,6 @@ com.google.api.services.bigquery.model.JobStatistics toPb() { return statistics; } - static Builder builder() { - return new Builder(); - } - @SuppressWarnings("unchecked") static T fromPb( com.google.api.services.bigquery.model.JobStatistics statisticPb) { @@ -510,7 +557,7 @@ static T fromPb( } else if (statisticPb.getQuery() != null) { return (T) QueryStatistics.fromPb(statisticPb); } else { - return (T) new Builder(statisticPb).build(); + return (T) CopyStatistics.fromPb(statisticPb); } } } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java index 738a644a5dde..7c948e6373f9 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatus.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/JobStatus.java @@ -1,4 +1,4 @@ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; @@ -94,13 +94,16 @@ public String toString() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(state, error, executionErrors); } @Override - public boolean equals(Object obj) { - return obj instanceof JobStatus && Objects.equals(toPb(), ((JobStatus) obj).toPb()); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(JobStatus.class) + && Objects.equals(toPb(), ((JobStatus) obj).toPb()); } com.google.api.services.bigquery.model.JobStatus toPb() { diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java similarity index 96% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java index 223a25a478e0..99baa2a7086f 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadConfiguration.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import java.util.List; /** * Common interface for a load configuration. A load configuration * ({@link WriteChannelConfiguration}) can be used to load data into a table with a - * {@link com.google.gcloud.WriteChannel} ({@link BigQuery#writer(WriteChannelConfiguration)}). + * {@link com.google.cloud.WriteChannel} ({@link BigQuery#writer(WriteChannelConfiguration)}). * A load configuration ({@link LoadJobConfiguration}) can also be used to create a load job * ({@link JobInfo#of(JobConfiguration)}). */ diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadJobConfiguration.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadJobConfiguration.java index 9c9fa7a769b6..03e2d7fea05e 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/LoadJobConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/LoadJobConfiguration.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -269,7 +269,9 @@ ToStringHelper toStringHelper() { @Override public boolean equals(Object obj) { - return obj instanceof LoadJobConfiguration && baseEquals((LoadJobConfiguration) obj); + return obj == this + || obj instanceof LoadJobConfiguration + && baseEquals((LoadJobConfiguration) obj); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Option.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Option.java similarity index 87% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Option.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Option.java index 3fdc27ecab99..75108d6dfec5 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Option.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Option.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.bigquery.spi.BigQueryRpc; import com.google.common.base.MoreObjects; -import com.google.gcloud.bigquery.spi.BigQueryRpc; import java.io.Serializable; import java.util.Objects; @@ -27,7 +27,7 @@ /** * Base class for BigQuery operation option. */ -class Option implements Serializable { +abstract class Option implements Serializable { private static final long serialVersionUID = -6647817677804099207L; @@ -53,8 +53,7 @@ public boolean equals(Object obj) { return false; } Option other = (Option) obj; - return Objects.equals(rpcOption, other.rpcOption) - && Objects.equals(value, other.value); + return Objects.equals(rpcOption, other.rpcOption) && Objects.equals(value, other.value); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryJobConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryJobConfiguration.java similarity index 98% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryJobConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryJobConfiguration.java index 688611d07526..73b1403245c7 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryJobConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryJobConfiguration.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.bigquery.model.JobConfigurationQuery; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; import java.util.List; import java.util.Map; @@ -39,6 +39,19 @@ public final class QueryJobConfiguration extends JobConfiguration { private static final long serialVersionUID = -1108948249081804890L; + private final String query; + private final TableId destinationTable; + private final Map tableDefinitions; + private final List userDefinedFunctions; + private final CreateDisposition createDisposition; + private final WriteDisposition writeDisposition; + private final DatasetId defaultDataset; + private final Priority priority; + private final Boolean allowLargeResults; + private final Boolean useQueryCache; + private final Boolean flattenResults; + private final Boolean dryRun; + /** * Priority levels for a query. If not specified the priority is assumed to be * {@link Priority#INTERACTIVE}. @@ -59,19 +72,6 @@ public enum Priority { BATCH } - private final String query; - private final TableId destinationTable; - private final Map tableDefinitions; - private final List userDefinedFunctions; - private final CreateDisposition createDisposition; - private final WriteDisposition writeDisposition; - private final DatasetId defaultDataset; - private final Priority priority; - private final Boolean allowLargeResults; - private final Boolean useQueryCache; - private final Boolean flattenResults; - private final Boolean dryRun; - public static final class Builder extends JobConfiguration.Builder { @@ -450,7 +450,9 @@ ToStringHelper toStringHelper() { @Override public boolean equals(Object obj) { - return obj instanceof QueryJobConfiguration && baseEquals((QueryJobConfiguration) obj); + return obj == this + || obj instanceof QueryJobConfiguration + && baseEquals((QueryJobConfiguration) obj); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryRequest.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequest.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryRequest.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequest.java index 5f99f3c5b4ee..166e0db9a18d 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryRequest.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -40,7 +40,7 @@ * QueryRequest request = QueryRequest.builder("SELECT field FROM table") * .defaultDataset(DatasetId.of("dataset")) * .maxWaitTime(60000L) - * .maxResults(1000L) + * .pageSize(1000L) * .build(); * QueryResponse response = bigquery.query(request); * while (!response.jobCompleted()) { @@ -60,12 +60,12 @@ * @see Query * @see Query Reference */ -public class QueryRequest implements Serializable { +public final class QueryRequest implements Serializable { private static final long serialVersionUID = -8727328332415880852L; private final String query; - private final Long maxResults; + private final Long pageSize; private final DatasetId defaultDataset; private final Long maxWaitTime; private final Boolean dryRun; @@ -74,7 +74,7 @@ public class QueryRequest implements Serializable { public static final class Builder { private String query; - private Long maxResults; + private Long pageSize; private DatasetId defaultDataset; private Long maxWaitTime; private Boolean dryRun; @@ -96,8 +96,8 @@ public Builder query(String query) { * query result set is large. In addition to this limit, responses are also limited to 10 MB. * By default, there is no maximum row count, and only the byte limit applies. */ - public Builder maxResults(Long maxResults) { - this.maxResults = maxResults; + public Builder pageSize(Long pageSize) { + this.pageSize = pageSize; return this; } @@ -157,7 +157,7 @@ public QueryRequest build() { private QueryRequest(Builder builder) { query = builder.query; - maxResults = builder.maxResults; + pageSize = builder.pageSize; defaultDataset = builder.defaultDataset; maxWaitTime = builder.maxWaitTime; dryRun = builder.dryRun; @@ -174,8 +174,8 @@ public String query() { /** * Returns the maximum number of rows of data to return per page of results. */ - public Long maxResults() { - return maxResults; + public Long pageSize() { + return pageSize; } /** @@ -224,7 +224,7 @@ public Boolean useQueryCache() { public Builder toBuilder() { return new Builder() .query(query) - .maxResults(maxResults) + .pageSize(pageSize) .defaultDataset(defaultDataset) .maxWaitTime(maxWaitTime) .dryRun(dryRun) @@ -235,7 +235,7 @@ public Builder toBuilder() { public String toString() { return MoreObjects.toStringHelper(this) .add("query", query) - .add("maxResults", maxResults) + .add("pageSize", pageSize) .add("defaultDataset", defaultDataset) .add("maxWaitTime", maxWaitTime) .add("dryRun", dryRun) @@ -245,12 +245,14 @@ public String toString() { @Override public int hashCode() { - return Objects.hash(query, maxResults, defaultDataset, maxWaitTime, dryRun, useQueryCache); + return Objects.hash(query, pageSize, defaultDataset, maxWaitTime, dryRun, useQueryCache); } @Override public boolean equals(Object obj) { - return obj instanceof QueryRequest && Objects.equals(toPb(), ((QueryRequest) obj).toPb()); + return obj == this + || obj instanceof QueryRequest + && Objects.equals(toPb(), ((QueryRequest) obj).toPb()); } QueryRequest setProjectId(String projectId) { @@ -264,8 +266,8 @@ QueryRequest setProjectId(String projectId) { com.google.api.services.bigquery.model.QueryRequest toPb() { com.google.api.services.bigquery.model.QueryRequest queryRequestPb = new com.google.api.services.bigquery.model.QueryRequest().setQuery(query); - if (maxResults != null) { - queryRequestPb.setMaxResults(maxResults); + if (pageSize != null) { + queryRequestPb.setMaxResults(pageSize); } if (defaultDataset != null) { queryRequestPb.setDefaultDataset(defaultDataset.toPb()); @@ -299,7 +301,7 @@ public static QueryRequest of(String query) { static QueryRequest fromPb(com.google.api.services.bigquery.model.QueryRequest queryRequestPb) { Builder builder = builder(queryRequestPb.getQuery()); if (queryRequestPb.getMaxResults() != null) { - builder.maxResults(queryRequestPb.getMaxResults()); + builder.pageSize(queryRequestPb.getMaxResults()); } if (queryRequestPb.getDefaultDataset() != null) { builder.defaultDataset(DatasetId.fromPb(queryRequestPb.getDefaultDataset())); diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResponse.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResponse.java similarity index 96% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResponse.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResponse.java index 12000cc1cbd2..57a8966b0301 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResponse.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResponse.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; @@ -170,16 +170,16 @@ public String toString() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(jobId); } @Override - public boolean equals(Object obj) { - if (this == obj) { + public final boolean equals(Object obj) { + if (obj == this) { return true; } - if (obj == null || getClass() != obj.getClass()) { + if (obj == null || !obj.getClass().equals(QueryResponse.class)) { return false; } QueryResponse response = (QueryResponse) obj; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResult.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResult.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResult.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResult.java index 692abab937a9..ba036187307c 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryResult.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryResult.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; +import com.google.cloud.PageImpl; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; -import com.google.gcloud.PageImpl; import java.util.List; import java.util.Objects; @@ -149,16 +149,16 @@ public String toString() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), cacheHit, schema, totalBytesProcessed, totalRows); } @Override - public boolean equals(Object obj) { - if (this == obj) { + public final boolean equals(Object obj) { + if (obj == this) { return true; } - if (obj == null || getClass() != obj.getClass()) { + if (obj == null || !obj.getClass().equals(QueryResult.class)) { return false; } QueryResult response = (QueryResult) obj; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryStage.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryStage.java index 8c9f91fd39f3..7276a86e7eeb 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/QueryStage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.ExplainQueryStage; import com.google.api.services.bigquery.model.ExplainQueryStep; @@ -137,7 +137,7 @@ static QueryStep fromPb(com.google.api.services.bigquery.model.ExplainQueryStep private final double computeRatioAvg; private final double computeRatioMax; - private final long id; + private final long generatedId; private final String name; private final double readRatioAvg; private final double readRatioMax; @@ -153,7 +153,7 @@ static final class Builder { private double computeRatioAvg; private double computeRatioMax; - private long id; + private long generatedId; private String name; private double readRatioAvg; private double readRatioMax; @@ -177,8 +177,8 @@ Builder computeRatioMax(double computeRatioMax) { return this; } - Builder id(long id) { - this.id = id; + Builder generatedId(long generatedId) { + this.generatedId = generatedId; return this; } @@ -240,7 +240,7 @@ QueryStage build() { QueryStage(Builder builder) { computeRatioAvg = builder.computeRatioAvg; computeRatioMax = builder.computeRatioMax; - id = builder.id; + generatedId = builder.generatedId; name = builder.name; readRatioAvg = builder.readRatioAvg; readRatioMax = builder.readRatioMax; @@ -270,10 +270,10 @@ public double computeRatioMax() { } /** - * Returns a unique ID for the stage within its plan. + * Returns a unique, server-generated ID for the stage within its plan. */ - public long id() { - return id; + public long generatedId() { + return generatedId; } /** @@ -357,7 +357,7 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("computeRatioAvg", computeRatioAvg) .add("computeRatioMax", computeRatioMax) - .add("id", id) + .add("generatedId", generatedId) .add("name", name) .add("readRatioAvg", readRatioAvg) .add("readRatioMax", readRatioMax) @@ -372,18 +372,22 @@ public String toString() { } @Override - public int hashCode() { - return Objects.hash(computeRatioAvg, computeRatioMax, id, name, readRatioAvg, readRatioMax, - recordsRead, recordsWritten, steps, waitRatioAvg, waitRatioMax, writeRatioAvg); + public final int hashCode() { + return Objects.hash(computeRatioAvg, computeRatioMax, generatedId, name, readRatioAvg, + readRatioMax, recordsRead, recordsWritten, steps, waitRatioAvg, waitRatioMax, + writeRatioAvg); } @Override - public boolean equals(Object obj) { - if (!(obj instanceof QueryStage)) { + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(QueryStage.class)) { return false; } QueryStage other = (QueryStage) obj; - return id == other.id + return generatedId == other.generatedId && computeRatioAvg == other.computeRatioAvg && computeRatioMax == other.computeRatioMax && readRatioAvg == other.readRatioAvg @@ -406,7 +410,7 @@ ExplainQueryStage toPb() { ExplainQueryStage stagePb = new ExplainQueryStage() .setComputeRatioAvg(computeRatioAvg) .setComputeRatioMax(computeRatioMax) - .setId(id) + .setId(generatedId) .setName(name) .setReadRatioAvg(readRatioAvg) .setReadRatioMax(readRatioMax) @@ -426,7 +430,7 @@ static QueryStage fromPb(com.google.api.services.bigquery.model.ExplainQueryStag Builder builder = new QueryStage.Builder(); builder.computeRatioAvg(stagePb.getComputeRatioAvg()); builder.computeRatioMax(stagePb.getComputeRatioMax()); - builder.id(stagePb.getId()); + builder.generatedId(stagePb.getId()); builder.name(stagePb.getName()); builder.readRatioAvg(stagePb.getReadRatioAvg()); builder.readRatioMax(stagePb.getReadRatioMax()); diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Schema.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Schema.java index 787bb0d7f35f..218c2a214e50 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/Schema.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Schema.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -31,7 +31,7 @@ /** * This class represents the schema for a Google BigQuery Table or data source. */ -public class Schema implements Serializable { +public final class Schema implements Serializable { static final Function FROM_PB_FUNCTION = new Function */ -public final class Table extends TableInfo { +public class Table extends TableInfo { private static final long serialVersionUID = 5744556727066570096L; @@ -92,8 +92,8 @@ public Builder friendlyName(String friendlyName) { } @Override - Builder id(String id) { - infoBuilder.id(id); + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); return this; } @@ -322,14 +322,20 @@ public Builder toBuilder() { } @Override - public boolean equals(Object obj) { - return obj instanceof Table - && Objects.equals(toPb(), ((Table) obj).toPb()) - && Objects.equals(options, ((Table) obj).options); + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Table.class)) { + return false; + } + Table other = (Table) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), options); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDataWriteChannel.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDataWriteChannel.java similarity index 91% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDataWriteChannel.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDataWriteChannel.java index 9c6a950ca27f..9319b59cfea3 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDataWriteChannel.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDataWriteChannel.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; -import static com.google.gcloud.RetryHelper.runWithRetries; +import static com.google.cloud.RetryHelper.runWithRetries; import static java.util.concurrent.Executors.callable; -import com.google.gcloud.BaseWriteChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.WriteChannel; +import com.google.cloud.BaseWriteChannel; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryHelper; +import com.google.cloud.WriteChannel; /** * WriteChannel implementation to stream data into a BigQuery table. diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDefinition.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDefinition.java similarity index 99% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDefinition.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDefinition.java index 26e7bcc76f55..2570c50373a7 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableDefinition.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableDefinition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableId.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableId.java similarity index 93% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableId.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableId.java index 20ed53cc1a5d..95768a59b5d2 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableId.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableId.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -27,7 +27,7 @@ /** * Google BigQuery Table identity. */ -public class TableId implements Serializable { +public final class TableId implements Serializable { static final Function FROM_PB_FUNCTION = new Function() { @@ -92,7 +92,9 @@ public static TableId of(String dataset, String table) { @Override public boolean equals(Object obj) { - return obj instanceof TableId && Objects.equals(toPb(), ((TableId) obj).toPb()); + return obj == this + || obj instanceof TableId + && Objects.equals(toPb(), ((TableId) obj).toPb()); } @Override diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableInfo.java similarity index 94% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableInfo.java index de331350e978..c27b0dfdf1e5 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/TableInfo.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/TableInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; @@ -52,10 +52,10 @@ public Table apply(TableInfo tableInfo) { } }; - private static final long serialVersionUID = -7679032506430816205L; + private static final long serialVersionUID = 609769795097719407L; private final String etag; - private final String id; + private final String generatedId; private final String selfLink; private final TableId tableId; private final String friendlyName; @@ -90,7 +90,7 @@ public abstract static class Builder { */ public abstract Builder friendlyName(String friendlyName); - abstract Builder id(String id); + abstract Builder generatedId(String generatedId); abstract Builder lastModifiedTime(Long lastModifiedTime); @@ -117,7 +117,7 @@ public abstract static class Builder { static class BuilderImpl extends Builder { private String etag; - private String id; + private String generatedId; private String selfLink; private TableId tableId; private String friendlyName; @@ -131,7 +131,7 @@ static class BuilderImpl extends Builder { BuilderImpl(TableInfo tableInfo) { this.etag = tableInfo.etag; - this.id = tableInfo.id; + this.generatedId = tableInfo.generatedId; this.selfLink = tableInfo.selfLink; this.tableId = tableInfo.tableId; this.friendlyName = tableInfo.friendlyName; @@ -152,7 +152,7 @@ static class BuilderImpl extends Builder { this.friendlyName = tablePb.getFriendlyName(); this.creationTime = tablePb.getCreationTime(); this.etag = tablePb.getEtag(); - this.id = tablePb.getId(); + this.generatedId = tablePb.getId(); this.selfLink = tablePb.getSelfLink(); this.definition = TableDefinition.fromPb(tablePb); } @@ -188,8 +188,8 @@ public Builder friendlyName(String friendlyName) { } @Override - Builder id(String id) { - this.id = id; + Builder generatedId(String generatedId) { + this.generatedId = generatedId; return this; } @@ -226,7 +226,7 @@ public TableInfo build() { TableInfo(BuilderImpl builder) { this.tableId = checkNotNull(builder.tableId); this.etag = builder.etag; - this.id = builder.id; + this.generatedId = builder.generatedId; this.selfLink = builder.selfLink; this.friendlyName = builder.friendlyName; this.description = builder.description; @@ -244,10 +244,10 @@ public String etag() { } /** - * Returns an opaque id for the table. + * Returns the service-generated id for the table. */ - public String id() { - return id; + public String generatedId() { + return generatedId; } /** @@ -321,7 +321,7 @@ public String toString() { return MoreObjects.toStringHelper(this) .add("tableId", tableId) .add("etag", etag) - .add("id", id) + .add("generatedId", generatedId) .add("selfLink", selfLink) .add("friendlyName", friendlyName) .add("description", description) @@ -339,7 +339,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj != null + return obj == this + || obj != null && obj.getClass().equals(TableInfo.class) && Objects.equals(toPb(), ((TableInfo) obj).toPb()); } @@ -379,7 +380,7 @@ Table toPb() { tablePb.setEtag(etag); tablePb.setExpirationTime(expirationTime); tablePb.setFriendlyName(friendlyName); - tablePb.setId(id); + tablePb.setId(generatedId); tablePb.setSelfLink(selfLink); return tablePb; } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java similarity index 99% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java index 2135e0ddc941..09fa2563c59f 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/UserDefinedFunction.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/UserDefinedFunction.java @@ -1,4 +1,4 @@ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import com.google.api.services.bigquery.model.UserDefinedFunctionResource; import com.google.common.base.Function; diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewDefinition.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ViewDefinition.java similarity index 95% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewDefinition.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ViewDefinition.java index 796dd411b4a1..1d3a97ba616d 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/ViewDefinition.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/ViewDefinition.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; @@ -33,7 +33,7 @@ * * @see Views */ -public final class ViewDefinition extends TableDefinition { +public class ViewDefinition extends TableDefinition { private static final long serialVersionUID = -8789311196910794545L; @@ -146,12 +146,15 @@ ToStringHelper toStringHelper() { } @Override - public boolean equals(Object obj) { - return obj instanceof ViewDefinition && baseEquals((ViewDefinition) obj); + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ViewDefinition.class) + && baseEquals((ViewDefinition) obj); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(baseHashCode(), query, userDefinedFunctions); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/WriteChannelConfiguration.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/WriteChannelConfiguration.java similarity index 96% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/WriteChannelConfiguration.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/WriteChannelConfiguration.java index 6cc44ce7d5d6..898063e7e0ed 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/WriteChannelConfiguration.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/WriteChannelConfiguration.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.bigquery.model.JobConfigurationLoad; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; import java.io.Serializable; import java.util.List; @@ -30,10 +30,10 @@ /** * Google BigQuery Configuration for a load operation. A load configuration can be used to load data - * into a table with a {@link com.google.gcloud.WriteChannel} + * into a table with a {@link com.google.cloud.WriteChannel} * ({@link BigQuery#writer(WriteChannelConfiguration)}). */ -public class WriteChannelConfiguration implements LoadConfiguration, Serializable { +public final class WriteChannelConfiguration implements LoadConfiguration, Serializable { private static final long serialVersionUID = 470267591917413578L; @@ -241,7 +241,8 @@ public String toString() { @Override public boolean equals(Object obj) { - return obj instanceof WriteChannelConfiguration + return obj == this + || obj instanceof WriteChannelConfiguration && Objects.equals(toPb(), ((WriteChannelConfiguration) obj).toPb()); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/package-info.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java similarity index 92% rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/package-info.java rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java index db5e956e0a12..a701b82c1c2c 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/package-info.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java @@ -19,7 +19,7 @@ * *

A simple usage example showing how to create a table if it does not exist and load data into * it. For the complete source code see - * + * * CreateTableAndLoadData.java. *

 {@code
  * BigQuery bigquery = BigQueryOptions.defaultInstance().service();
@@ -44,4 +44,4 @@
  *
  * @see Google Cloud BigQuery
  */
-package com.google.gcloud.bigquery;
+package com.google.cloud.bigquery;
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpc.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpc.java
similarity index 98%
rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpc.java
rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpc.java
index d0b740e9e390..e221079cdd03 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpc.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpc.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery.spi;
+package com.google.cloud.bigquery.spi;
 
 import com.google.api.services.bigquery.model.Dataset;
 import com.google.api.services.bigquery.model.GetQueryResultsResponse;
@@ -26,7 +26,7 @@
 import com.google.api.services.bigquery.model.TableDataInsertAllRequest;
 import com.google.api.services.bigquery.model.TableDataInsertAllResponse;
 import com.google.api.services.bigquery.model.TableRow;
-import com.google.gcloud.bigquery.BigQueryException;
+import com.google.cloud.bigquery.BigQueryException;
 
 import java.util.Map;
 
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpcFactory.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpcFactory.java
similarity index 85%
rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpcFactory.java
rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpcFactory.java
index 1323ec0624f4..8c9fb2413a41 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/BigQueryRpcFactory.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/BigQueryRpcFactory.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery.spi;
+package com.google.cloud.bigquery.spi;
 
-import com.google.gcloud.bigquery.BigQueryOptions;
-import com.google.gcloud.spi.ServiceRpcFactory;
+import com.google.cloud.bigquery.BigQueryOptions;
+import com.google.cloud.spi.ServiceRpcFactory;
 
 /**
  * An interface for BigQuery RPC factory.
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/DefaultBigQueryRpc.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/DefaultBigQueryRpc.java
similarity index 95%
rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/DefaultBigQueryRpc.java
rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/DefaultBigQueryRpc.java
index 71712bda7806..2f02f55c68e7 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/spi/DefaultBigQueryRpc.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/spi/DefaultBigQueryRpc.java
@@ -12,17 +12,17 @@
  * the License.
  */
 
-package com.google.gcloud.bigquery.spi;
+package com.google.cloud.bigquery.spi;
 
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.ALL_DATASETS;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.ALL_USERS;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.DELETE_CONTENTS;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.FIELDS;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.MAX_RESULTS;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.PAGE_TOKEN;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.START_INDEX;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.STATE_FILTER;
-import static com.google.gcloud.bigquery.spi.BigQueryRpc.Option.TIMEOUT;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.ALL_DATASETS;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.ALL_USERS;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.DELETE_CONTENTS;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.FIELDS;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.MAX_RESULTS;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.PAGE_TOKEN;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.START_INDEX;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.STATE_FILTER;
+import static com.google.cloud.bigquery.spi.BigQueryRpc.Option.TIMEOUT;
 import static java.net.HttpURLConnection.HTTP_CREATED;
 import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
 import static java.net.HttpURLConnection.HTTP_OK;
@@ -56,13 +56,12 @@
 import com.google.api.services.bigquery.model.TableList;
 import com.google.api.services.bigquery.model.TableReference;
 import com.google.api.services.bigquery.model.TableRow;
+import com.google.cloud.bigquery.BigQueryException;
+import com.google.cloud.bigquery.BigQueryOptions;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
-import com.google.gcloud.bigquery.BigQueryException;
-import com.google.gcloud.bigquery.BigQueryOptions;
-
 import java.io.IOException;
 import java.math.BigInteger;
 import java.util.List;
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/RemoteBigQueryHelper.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java
similarity index 94%
rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/RemoteBigQueryHelper.java
rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java
index 491e822d683c..d844ad56235a 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/RemoteBigQueryHelper.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelper.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery.testing;
+package com.google.cloud.bigquery.testing;
 
-import com.google.gcloud.AuthCredentials;
-import com.google.gcloud.RetryParams;
-import com.google.gcloud.bigquery.BigQuery;
-import com.google.gcloud.bigquery.BigQueryException;
-import com.google.gcloud.bigquery.BigQueryOptions;
+import com.google.cloud.AuthCredentials;
+import com.google.cloud.RetryParams;
+import com.google.cloud.bigquery.BigQuery;
+import com.google.cloud.bigquery.BigQueryException;
+import com.google.cloud.bigquery.BigQueryOptions;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/package-info.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/package-info.java
similarity index 96%
rename from gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/package-info.java
rename to gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/package-info.java
index 9ca792ecd77d..be00683c9ce8 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/testing/package-info.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/testing/package-info.java
@@ -35,4 +35,4 @@
  * @see 
  *     gcloud-java tools for testing
  */
-package com.google.gcloud.bigquery.testing;
+package com.google.cloud.bigquery.testing;
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/AclTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java
similarity index 88%
rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/AclTest.java
rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java
index 438526b95b6e..51b5e1ad496e 100644
--- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/AclTest.java
+++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery;
+package com.google.cloud.bigquery;
 
 import static org.junit.Assert.assertEquals;
 
 import com.google.api.services.bigquery.model.Dataset;
-import com.google.gcloud.bigquery.Acl.Domain;
-import com.google.gcloud.bigquery.Acl.Entity;
-import com.google.gcloud.bigquery.Acl.Entity.Type;
-import com.google.gcloud.bigquery.Acl.Group;
-import com.google.gcloud.bigquery.Acl.Role;
-import com.google.gcloud.bigquery.Acl.User;
-import com.google.gcloud.bigquery.Acl.View;
+import com.google.cloud.bigquery.Acl.Domain;
+import com.google.cloud.bigquery.Acl.Entity;
+import com.google.cloud.bigquery.Acl.Entity.Type;
+import com.google.cloud.bigquery.Acl.Group;
+import com.google.cloud.bigquery.Acl.Role;
+import com.google.cloud.bigquery.Acl.User;
+import com.google.cloud.bigquery.Acl.View;
 
 import org.junit.Test;
 
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryErrorTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java
similarity index 97%
rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryErrorTest.java
rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java
index c8de039e233f..1d30bb87815e 100644
--- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryErrorTest.java
+++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryErrorTest.java
@@ -1,4 +1,4 @@
-package com.google.gcloud.bigquery;
+package com.google.cloud.bigquery;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryExceptionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryExceptionTest.java
similarity index 96%
rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryExceptionTest.java
rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryExceptionTest.java
index 66e5289424e2..b0a32cd7f0c7 100644
--- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryExceptionTest.java
+++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryExceptionTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery;
+package com.google.cloud.bigquery;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -25,8 +25,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import com.google.gcloud.BaseServiceException;
-import com.google.gcloud.RetryHelper.RetryHelperException;
+import com.google.cloud.BaseServiceException;
+import com.google.cloud.RetryHelper.RetryHelperException;
 
 import org.junit.Test;
 
diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryImplTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java
similarity index 96%
rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryImplTest.java
rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java
index 305745e72da9..4a29697a73dc 100644
--- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/BigQueryImplTest.java
+++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.bigquery;
+package com.google.cloud.bigquery;
 
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.eq;
@@ -31,18 +31,18 @@
 import com.google.api.services.bigquery.model.TableDataInsertAllRequest;
 import com.google.api.services.bigquery.model.TableDataInsertAllResponse;
 import com.google.api.services.bigquery.model.TableRow;
+import com.google.cloud.Page;
+import com.google.cloud.RetryParams;
+import com.google.cloud.WriteChannel;
+import com.google.cloud.bigquery.InsertAllRequest.RowToInsert;
+import com.google.cloud.bigquery.spi.BigQueryRpc;
+import com.google.cloud.bigquery.spi.BigQueryRpc.Tuple;
+import com.google.cloud.bigquery.spi.BigQueryRpcFactory;
 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.Lists;
-import com.google.gcloud.Page;
-import com.google.gcloud.RetryParams;
-import com.google.gcloud.WriteChannel;
-import com.google.gcloud.bigquery.InsertAllRequest.RowToInsert;
-import com.google.gcloud.bigquery.spi.BigQueryRpc;
-import com.google.gcloud.bigquery.spi.BigQueryRpc.Tuple;
-import com.google.gcloud.bigquery.spi.BigQueryRpcFactory;
 
 import org.easymock.Capture;
 import org.easymock.EasyMock;
@@ -148,12 +148,12 @@ public class BigQueryImplTest {
   private static final TableRow TABLE_ROW =
       new TableRow().setF(ImmutableList.of(BOOLEAN_FIELD, INTEGER_FIELD));
   private static final QueryRequest QUERY_REQUEST = QueryRequest.builder("SQL")
-      .maxResults(42L)
+      .pageSize(42L)
       .useQueryCache(false)
       .defaultDataset(DatasetId.of(DATASET))
       .build();
   private static final QueryRequest QUERY_REQUEST_WITH_PROJECT = QueryRequest.builder("SQL")
-      .maxResults(42L)
+      .pageSize(42L)
       .useQueryCache(false)
       .defaultDataset(DatasetId.of(PROJECT, DATASET))
       .build();
@@ -169,9 +169,9 @@ public class BigQueryImplTest {
   private static final BigQuery.DatasetListOption DATASET_LIST_ALL =
       BigQuery.DatasetListOption.all();
   private static final BigQuery.DatasetListOption DATASET_LIST_PAGE_TOKEN =
-      BigQuery.DatasetListOption.startPageToken("cursor");
-  private static final BigQuery.DatasetListOption DATASET_LIST_MAX_RESULTS =
-      BigQuery.DatasetListOption.maxResults(42L);
+      BigQuery.DatasetListOption.pageToken("cursor");
+  private static final BigQuery.DatasetListOption DATASET_LIST_PAGE_SIZE =
+      BigQuery.DatasetListOption.pageSize(42L);
   private static final Map DATASET_LIST_OPTIONS = ImmutableMap.of(
       BigQueryRpc.Option.ALL_DATASETS, true,
       BigQueryRpc.Option.PAGE_TOKEN, "cursor",
@@ -188,19 +188,19 @@ public class BigQueryImplTest {
       BigQuery.TableOption.fields(BigQuery.TableField.SCHEMA, BigQuery.TableField.ETAG);
 
   // Table list options
-  private static final BigQuery.TableListOption TABLE_LIST_MAX_RESULTS =
-      BigQuery.TableListOption.maxResults(42L);
+  private static final BigQuery.TableListOption TABLE_LIST_PAGE_SIZE =
+      BigQuery.TableListOption.pageSize(42L);
   private static final BigQuery.TableListOption TABLE_LIST_PAGE_TOKEN =
-      BigQuery.TableListOption.startPageToken("cursor");
+      BigQuery.TableListOption.pageToken("cursor");
   private static final Map TABLE_LIST_OPTIONS = ImmutableMap.of(
       BigQueryRpc.Option.MAX_RESULTS, 42L,
       BigQueryRpc.Option.PAGE_TOKEN, "cursor");
 
   // TableData list options
-  private static final BigQuery.TableDataListOption TABLE_DATA_LIST_MAX_RESULTS =
-      BigQuery.TableDataListOption.maxResults(42L);
+  private static final BigQuery.TableDataListOption TABLE_DATA_LIST_PAGE_SIZE =
+      BigQuery.TableDataListOption.pageSize(42L);
   private static final BigQuery.TableDataListOption TABLE_DATA_LIST_PAGE_TOKEN =
-      BigQuery.TableDataListOption.startPageToken("cursor");
+      BigQuery.TableDataListOption.pageToken("cursor");
   private static final BigQuery.TableDataListOption TABLE_DATA_LIST_START_INDEX =
       BigQuery.TableDataListOption.startIndex(0L);
   private static final Map TABLE_DATA_LIST_OPTIONS = ImmutableMap.of(
@@ -220,9 +220,9 @@ public class BigQueryImplTest {
   private static final BigQuery.JobListOption JOB_LIST_STATE_FILTER =
       BigQuery.JobListOption.stateFilter(JobStatus.State.DONE, JobStatus.State.PENDING);
   private static final BigQuery.JobListOption JOB_LIST_PAGE_TOKEN =
-      BigQuery.JobListOption.startPageToken("cursor");
-  private static final BigQuery.JobListOption JOB_LIST_MAX_RESULTS =
-      BigQuery.JobListOption.maxResults(42L);
+      BigQuery.JobListOption.pageToken("cursor");
+  private static final BigQuery.JobListOption JOB_LIST_PAGE_SIZE =
+      BigQuery.JobListOption.pageSize(42L);
   private static final Map JOB_LIST_OPTIONS = ImmutableMap.of(
       BigQueryRpc.Option.ALL_USERS, true,
       BigQueryRpc.Option.STATE_FILTER, ImmutableList.of("done", "pending"),
@@ -235,9 +235,9 @@ public class BigQueryImplTest {
   private static final BigQuery.QueryResultsOption QUERY_RESULTS_OPTION_INDEX =
       BigQuery.QueryResultsOption.startIndex(1024L);
   private static final BigQuery.QueryResultsOption QUERY_RESULTS_OPTION_PAGE_TOKEN =
-      BigQuery.QueryResultsOption.startPageToken("cursor");
-  private static final BigQuery.QueryResultsOption QUERY_RESULTS_OPTION_MAX_RESULTS =
-      BigQuery.QueryResultsOption.maxResults(0L);
+      BigQuery.QueryResultsOption.pageToken("cursor");
+  private static final BigQuery.QueryResultsOption QUERY_RESULTS_OPTION_PAGE_SIZE =
+      BigQuery.QueryResultsOption.pageSize(0L);
   private static final Map QUERY_RESULTS_OPTIONS = ImmutableMap.of(
       BigQueryRpc.Option.TIMEOUT, 42L,
       BigQueryRpc.Option.START_INDEX, 1024L,
@@ -388,7 +388,7 @@ public void testListDatasetsWithOptions() {
     EasyMock.expect(bigqueryRpcMock.listDatasets(DATASET_LIST_OPTIONS)).andReturn(result);
     EasyMock.replay(bigqueryRpcMock);
     Page page = bigquery.listDatasets(DATASET_LIST_ALL, DATASET_LIST_PAGE_TOKEN,
-        DATASET_LIST_MAX_RESULTS);
+        DATASET_LIST_PAGE_SIZE);
     assertEquals(cursor, page.nextPageCursor());
     assertArrayEquals(datasetList.toArray(), Iterables.toArray(page.values(), DatasetInfo.class));
   }
@@ -560,7 +560,7 @@ public void testListTablesWithOptions() {
         Tuple.of(cursor, Iterables.transform(tableList, TableInfo.TO_PB_FUNCTION));
     EasyMock.expect(bigqueryRpcMock.listTables(DATASET, TABLE_LIST_OPTIONS)).andReturn(result);
     EasyMock.replay(bigqueryRpcMock);
-    Page
page = bigquery.listTables(DATASET, TABLE_LIST_MAX_RESULTS, TABLE_LIST_PAGE_TOKEN); + Page
page = bigquery.listTables(DATASET, TABLE_LIST_PAGE_SIZE, TABLE_LIST_PAGE_TOKEN); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(tableList.toArray(), Iterables.toArray(page.values(), Table.class)); } @@ -733,7 +733,7 @@ public void testListTableDataWithOptions() { EasyMock.replay(bigqueryRpcMock); bigquery = options.service(); Page> page = bigquery.listTableData(DATASET, TABLE, - TABLE_DATA_LIST_MAX_RESULTS, TABLE_DATA_LIST_PAGE_TOKEN, TABLE_DATA_LIST_START_INDEX); + TABLE_DATA_LIST_PAGE_SIZE, TABLE_DATA_LIST_PAGE_TOKEN, TABLE_DATA_LIST_START_INDEX); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(tableData.toArray(), Iterables.toArray(page.values(), List.class)); } @@ -859,7 +859,7 @@ public com.google.api.services.bigquery.model.Job apply(Job job) { EasyMock.expect(bigqueryRpcMock.listJobs(JOB_LIST_OPTIONS)).andReturn(result); EasyMock.replay(bigqueryRpcMock); Page page = bigquery.listJobs(JOB_LIST_ALL_USERS, JOB_LIST_STATE_FILTER, - JOB_LIST_PAGE_TOKEN, JOB_LIST_MAX_RESULTS); + JOB_LIST_PAGE_TOKEN, JOB_LIST_PAGE_SIZE); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(jobList.toArray(), Iterables.toArray(page.values(), Job.class)); } @@ -886,12 +886,14 @@ public com.google.api.services.bigquery.model.Job apply(Job job) { assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(jobList.toArray(), Iterables.toArray(page.values(), Job.class)); String selector = (String) capturedOptions.getValue().get(JOB_OPTION_FIELDS.rpcOption()); - assertTrue(selector.contains("etag,jobs(")); + assertTrue(selector.contains("nextPageToken,jobs(")); assertTrue(selector.contains("configuration")); assertTrue(selector.contains("jobReference")); assertTrue(selector.contains("statistics")); - assertTrue(selector.contains("state,errorResult),nextPageToken")); - assertEquals(80, selector.length()); + assertTrue(selector.contains("state")); + assertTrue(selector.contains("errorResult")); + assertTrue(selector.contains(")")); + assertEquals(75, selector.length()); } @Test @@ -1012,7 +1014,7 @@ public void testGetQueryResultsWithOptions() { EasyMock.replay(bigqueryRpcMock); bigquery = options.service(); QueryResponse response = bigquery.getQueryResults(queryJob, QUERY_RESULTS_OPTION_TIME, - QUERY_RESULTS_OPTION_INDEX, QUERY_RESULTS_OPTION_MAX_RESULTS, + QUERY_RESULTS_OPTION_INDEX, QUERY_RESULTS_OPTION_PAGE_SIZE, QUERY_RESULTS_OPTION_PAGE_TOKEN); assertEquals(queryJob, response.jobId()); assertEquals(true, response.jobCompleted()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CopyJobConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CopyJobConfigurationTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CopyJobConfigurationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CopyJobConfigurationTest.java index 3f3f6f0fd15c..7d676a9b1505 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CopyJobConfigurationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CopyJobConfigurationTest.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; import org.junit.Test; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CsvOptionsTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CsvOptionsTest.java index 371202174431..df56a5ae096e 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/CsvOptionsTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/CsvOptionsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetIdTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetIdTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetIdTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetIdTest.java index ec645d71c96f..5cf627a42e38 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetIdTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetIdTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetInfoTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java similarity index 94% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetInfoTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java index 20875c0fc853..6e9c961f207f 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetInfoTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetInfoTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -38,7 +38,7 @@ public class DatasetInfoTest { private static final String DESCRIPTION = "description"; private static final String ETAG = "0xFF00"; private static final String FRIENDLY_NAME = "friendlyDataset"; - private static final String ID = "P/D:1"; + private static final String GENERATED_ID = "P/D:1"; private static final Long LAST_MODIFIED = CREATION_TIME + 50; private static final String LOCATION = ""; private static final String SELF_LINK = "http://bigquery/p/d"; @@ -51,7 +51,7 @@ public class DatasetInfoTest { .description(DESCRIPTION) .etag(ETAG) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModified(LAST_MODIFIED) .location(LOCATION) .selfLink(SELF_LINK) @@ -90,7 +90,7 @@ public void testBuilder() { assertEquals(DESCRIPTION, DATASET_INFO.description()); assertEquals(ETAG, DATASET_INFO.etag()); assertEquals(FRIENDLY_NAME, DATASET_INFO.friendlyName()); - assertEquals(ID, DATASET_INFO.id()); + assertEquals(GENERATED_ID, DATASET_INFO.generatedId()); assertEquals(LAST_MODIFIED, DATASET_INFO.lastModified()); assertEquals(LOCATION, DATASET_INFO.location()); assertEquals(SELF_LINK, DATASET_INFO.selfLink()); @@ -101,7 +101,7 @@ public void testBuilder() { assertEquals(DESCRIPTION, DATASET_INFO_COMPLETE.description()); assertEquals(ETAG, DATASET_INFO_COMPLETE.etag()); assertEquals(FRIENDLY_NAME, DATASET_INFO_COMPLETE.friendlyName()); - assertEquals(ID, DATASET_INFO_COMPLETE.id()); + assertEquals(GENERATED_ID, DATASET_INFO_COMPLETE.generatedId()); assertEquals(LAST_MODIFIED, DATASET_INFO_COMPLETE.lastModified()); assertEquals(LOCATION, DATASET_INFO_COMPLETE.location()); assertEquals(SELF_LINK, DATASET_INFO_COMPLETE.selfLink()); @@ -125,7 +125,7 @@ private void compareDatasets(DatasetInfo expected, DatasetInfo value) { assertEquals(expected.description(), value.description()); assertEquals(expected.etag(), value.etag()); assertEquals(expected.friendlyName(), value.friendlyName()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.location(), value.location()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.acl(), value.acl()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java index 373291021b23..7693e9900131 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/DatasetTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/DatasetTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -30,10 +30,10 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.gcloud.Page; -import com.google.gcloud.PageImpl; import org.junit.After; import org.junit.Test; @@ -51,7 +51,7 @@ public class DatasetTest { private static final String DESCRIPTION = "description"; private static final String ETAG = "0xFF00"; private static final String FRIENDLY_NAME = "friendlyDataset"; - private static final String ID = "P/D:1"; + private static final String GENERATED_ID = "P/D:1"; private static final Long LAST_MODIFIED = CREATION_TIME + 50; private static final String LOCATION = ""; private static final String SELF_LINK = "http://bigquery/p/d"; @@ -102,7 +102,7 @@ public void testBuilder() { .description(DESCRIPTION) .etag(ETAG) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModified(LAST_MODIFIED) .location(LOCATION) .selfLink(SELF_LINK) @@ -114,7 +114,7 @@ public void testBuilder() { assertEquals(DESCRIPTION, builtDataset.description()); assertEquals(ETAG, builtDataset.etag()); assertEquals(FRIENDLY_NAME, builtDataset.friendlyName()); - assertEquals(ID, builtDataset.id()); + assertEquals(GENERATED_ID, builtDataset.generatedId()); assertEquals(LAST_MODIFIED, builtDataset.lastModified()); assertEquals(LOCATION, builtDataset.location()); assertEquals(SELF_LINK, builtDataset.selfLink()); @@ -260,11 +260,11 @@ public void testListWithOptions() throws Exception { new Table(serviceMockReturnsOptions, new Table.BuilderImpl(TABLE_INFO3))); PageImpl
expectedPage = new PageImpl<>(null, "c", tableResults); expect(bigquery.options()).andReturn(mockOptions); - expect(bigquery.listTables(DATASET_INFO.datasetId(), BigQuery.TableListOption.maxResults(10L))) + expect(bigquery.listTables(DATASET_INFO.datasetId(), BigQuery.TableListOption.pageSize(10L))) .andReturn(expectedPage); replay(bigquery); initializeDataset(); - Page
tablePage = dataset.list(BigQuery.TableListOption.maxResults(10L)); + Page
tablePage = dataset.list(BigQuery.TableListOption.pageSize(10L)); assertArrayEquals(tableResults.toArray(), Iterables.toArray(tablePage.values(), Table.class)); assertEquals(expectedPage.nextPageCursor(), tablePage.nextPageCursor()); } @@ -362,7 +362,7 @@ private void compareDatasetInfo(DatasetInfo expected, DatasetInfo value) { assertEquals(expected.description(), value.description()); assertEquals(expected.etag(), value.etag()); assertEquals(expected.friendlyName(), value.friendlyName()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.location(), value.location()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.acl(), value.acl()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalTableDefinitionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExternalTableDefinitionTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalTableDefinitionTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExternalTableDefinitionTest.java index 247032dff890..e3e1c01b9403 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExternalTableDefinitionTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExternalTableDefinitionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExtractJobConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExtractJobConfigurationTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExtractJobConfigurationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExtractJobConfigurationTest.java index 7ac67f41b1f8..62d5274e491d 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ExtractJobConfigurationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ExtractJobConfigurationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldTest.java index 5f039eaed206..8e044d889313 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldValueTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldValueTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java index d6d879dbd58f..82086768b8ce 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FieldValueTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java index df939143156b..7406d8a7a283 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/FormatOptionsTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/FormatOptionsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllRequestTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllRequestTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllRequestTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllRequestTest.java index 0866f0b9349e..607b6c4145a8 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllRequestTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllRequestTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllResponseTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllResponseTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllResponseTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllResponseTest.java index b2eb0458f27f..dc30620200bc 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/InsertAllResponseTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/InsertAllResponseTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobIdTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobIdTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobIdTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobIdTest.java index 740830f07544..68caf62413c9 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobIdTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobIdTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobInfoTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobInfoTest.java similarity index 94% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobInfoTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobInfoTest.java index 260088470aff..d7fde0957a2f 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobInfoTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobInfoTest.java @@ -14,20 +14,21 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; +import com.google.cloud.bigquery.JobStatistics.CopyStatistics; +import com.google.cloud.bigquery.JobStatistics.ExtractStatistics; +import com.google.cloud.bigquery.JobStatistics.LoadStatistics; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; -import com.google.gcloud.bigquery.JobStatistics.ExtractStatistics; -import com.google.gcloud.bigquery.JobStatistics.LoadStatistics; -import com.google.gcloud.bigquery.JobStatistics.QueryStatistics; import org.junit.Test; @@ -37,12 +38,12 @@ public class JobInfoTest { private static final String ETAG = "etag"; - private static final String ID = "id"; + private static final String GENERATED_ID = "id"; private static final String SELF_LINK = "selfLink"; private static final String EMAIL = "email"; private static final JobId JOB_ID = JobId.of("job"); private static final JobStatus JOB_STATUS = new JobStatus(JobStatus.State.DONE); - private static final JobStatistics COPY_JOB_STATISTICS = JobStatistics.builder() + private static final CopyStatistics COPY_JOB_STATISTICS = CopyStatistics.builder() .creationTime(1L) .endTime(3L) .startTime(2L) @@ -163,7 +164,7 @@ public class JobInfoTest { .statistics(COPY_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) @@ -173,7 +174,7 @@ public class JobInfoTest { .statistics(EXTRACT_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) @@ -183,7 +184,7 @@ public class JobInfoTest { .statistics(LOAD_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) @@ -193,7 +194,7 @@ public class JobInfoTest { .statistics(QUERY_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) @@ -266,7 +267,7 @@ public void testToBuilderIncomplete() { @Test public void testBuilder() { assertEquals(ETAG, COPY_JOB.etag()); - assertEquals(ID, COPY_JOB.id()); + assertEquals(GENERATED_ID, COPY_JOB.generatedId()); assertEquals(SELF_LINK, COPY_JOB.selfLink()); assertEquals(EMAIL, COPY_JOB.userEmail()); assertEquals(JOB_ID, COPY_JOB.jobId()); @@ -275,7 +276,7 @@ public void testBuilder() { assertEquals(COPY_JOB_STATISTICS, COPY_JOB.statistics()); assertEquals(ETAG, EXTRACT_JOB.etag()); - assertEquals(ID, EXTRACT_JOB.id()); + assertEquals(GENERATED_ID, EXTRACT_JOB.generatedId()); assertEquals(SELF_LINK, EXTRACT_JOB.selfLink()); assertEquals(EMAIL, EXTRACT_JOB.userEmail()); assertEquals(JOB_ID, EXTRACT_JOB.jobId()); @@ -284,7 +285,7 @@ public void testBuilder() { assertEquals(EXTRACT_JOB_STATISTICS, EXTRACT_JOB.statistics()); assertEquals(ETAG, LOAD_JOB.etag()); - assertEquals(ID, LOAD_JOB.id()); + assertEquals(GENERATED_ID, LOAD_JOB.generatedId()); assertEquals(SELF_LINK, LOAD_JOB.selfLink()); assertEquals(EMAIL, LOAD_JOB.userEmail()); assertEquals(JOB_ID, LOAD_JOB.jobId()); @@ -293,7 +294,7 @@ public void testBuilder() { assertEquals(LOAD_JOB_STATISTICS, LOAD_JOB.statistics()); assertEquals(ETAG, QUERY_JOB.etag()); - assertEquals(ID, QUERY_JOB.id()); + assertEquals(GENERATED_ID, QUERY_JOB.generatedId()); assertEquals(SELF_LINK, QUERY_JOB.selfLink()); assertEquals(EMAIL, QUERY_JOB.userEmail()); assertEquals(JOB_ID, QUERY_JOB.jobId()); @@ -359,7 +360,7 @@ private void compareJobInfo(JobInfo expected, JobInfo value) { assertEquals(expected.hashCode(), value.hashCode()); assertEquals(expected.toString(), value.toString()); assertEquals(expected.etag(), value.etag()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.jobId(), value.jobId()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.status(), value.status()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatisticsTest.java similarity index 92% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatisticsTest.java index 1ec67d034754..c4c8c5ae6f4b 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatisticsTest.java @@ -14,15 +14,16 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.JobStatistics.ExtractStatistics; -import com.google.gcloud.bigquery.JobStatistics.LoadStatistics; -import com.google.gcloud.bigquery.JobStatistics.QueryStatistics; -import com.google.gcloud.bigquery.QueryStage.QueryStep; +import com.google.cloud.bigquery.JobStatistics.CopyStatistics; +import com.google.cloud.bigquery.JobStatistics.ExtractStatistics; +import com.google.cloud.bigquery.JobStatistics.LoadStatistics; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics; +import com.google.cloud.bigquery.QueryStage.QueryStep; import org.junit.Test; @@ -42,6 +43,11 @@ public class JobStatisticsTest { private static final Long CREATION_TIME = 10L; private static final Long END_TIME = 20L; private static final Long START_TIME = 15L; + private static final CopyStatistics COPY_STATISTICS = CopyStatistics.builder() + .creationTime(CREATION_TIME) + .endTime(END_TIME) + .startTime(START_TIME) + .build(); private static final ExtractStatistics EXTRACT_STATISTICS = ExtractStatistics.builder() .creationTime(CREATION_TIME) .endTime(END_TIME) @@ -71,7 +77,7 @@ public class JobStatisticsTest { private static final QueryStage QUERY_STAGE = QueryStage.builder() .computeRatioAvg(1.1) .computeRatioMax(2.2) - .id(42L) + .generatedId(42L) .name("stage") .readRatioAvg(3.3) .readRatioMax(4.4) @@ -101,17 +107,12 @@ public class JobStatisticsTest { .billingTier(BILLING_TIER) .cacheHit(CACHE_HIT) .build(); - private static final JobStatistics STATISTICS = JobStatistics.builder() - .creationTime(CREATION_TIME) - .endTime(END_TIME) - .startTime(START_TIME) - .build(); @Test public void testBuilder() { - assertEquals(CREATION_TIME, STATISTICS.creationTime()); - assertEquals(START_TIME, STATISTICS.startTime()); - assertEquals(END_TIME, STATISTICS.endTime()); + assertEquals(CREATION_TIME, COPY_STATISTICS.creationTime()); + assertEquals(START_TIME, COPY_STATISTICS.startTime()); + assertEquals(END_TIME, COPY_STATISTICS.endTime()); assertEquals(CREATION_TIME, EXTRACT_STATISTICS.creationTime()); assertEquals(START_TIME, EXTRACT_STATISTICS.startTime()); @@ -160,7 +161,7 @@ public void testToPbAndFromPb() { ExtractStatistics.fromPb(EXTRACT_STATISTICS.toPb())); compareLoadStatistics(LOAD_STATISTICS, LoadStatistics.fromPb(LOAD_STATISTICS.toPb())); compareQueryStatistics(QUERY_STATISTICS, QueryStatistics.fromPb(QUERY_STATISTICS.toPb())); - compareStatistics(STATISTICS, JobStatistics.fromPb(STATISTICS.toPb())); + compareStatistics(COPY_STATISTICS, JobStatistics.fromPb(COPY_STATISTICS.toPb())); compareLoadStatistics(LOAD_STATISTICS_INCOMPLETE, LoadStatistics.fromPb(LOAD_STATISTICS_INCOMPLETE.toPb())); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatusTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatusTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatusTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatusTest.java index c44386a3e72c..78a4345a339b 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatusTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobStatusTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java similarity index 95% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java index db51706fff5a..44e5e201e95c 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -27,6 +27,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.bigquery.JobStatistics.CopyStatistics; + import org.junit.After; import org.junit.Test; @@ -36,11 +38,11 @@ public class JobTest { private static final TableId TABLE_ID1 = TableId.of("dataset", "table1"); private static final TableId TABLE_ID2 = TableId.of("dataset", "table2"); private static final String ETAG = "etag"; - private static final String ID = "id"; + private static final String GENERATED_ID = "id"; private static final String SELF_LINK = "selfLink"; private static final String EMAIL = "email"; private static final JobStatus JOB_STATUS = new JobStatus(JobStatus.State.DONE); - private static final JobStatistics COPY_JOB_STATISTICS = JobStatistics.builder() + private static final JobStatistics COPY_JOB_STATISTICS = CopyStatistics.builder() .creationTime(1L) .endTime(3L) .startTime(2L) @@ -52,7 +54,7 @@ public class JobTest { .statistics(COPY_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) @@ -89,13 +91,13 @@ public void testBuilder() { .statistics(COPY_JOB_STATISTICS) .jobId(JOB_ID) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .selfLink(SELF_LINK) .userEmail(EMAIL) .status(JOB_STATUS) .build(); assertEquals(ETAG, builtJob.etag()); - assertEquals(ID, builtJob.id()); + assertEquals(GENERATED_ID, builtJob.generatedId()); assertEquals(SELF_LINK, builtJob.selfLink()); assertEquals(EMAIL, builtJob.userEmail()); assertEquals(JOB_ID, builtJob.jobId()); @@ -172,7 +174,7 @@ public void testIsDone_NotExists() throws Exception { expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); replay(bigquery); initializeJob(); - assertFalse(job.isDone()); + assertTrue(job.isDone()); } @Test @@ -247,7 +249,7 @@ private void compareJobInfo(JobInfo expected, JobInfo value) { assertEquals(expected.hashCode(), value.hashCode()); assertEquals(expected.toString(), value.toString()); assertEquals(expected.etag(), value.etag()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.jobId(), value.jobId()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.status(), value.status()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/LoadJobConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/LoadJobConfigurationTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/LoadJobConfigurationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/LoadJobConfigurationTest.java index 88ae6a4fc1b8..9980f85426d8 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/LoadJobConfigurationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/LoadJobConfigurationTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; import org.junit.Test; diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/OptionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/OptionTest.java new file mode 100644 index 000000000000..2e40d63ff80c --- /dev/null +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/OptionTest.java @@ -0,0 +1,65 @@ +/* + * 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.cloud.bigquery; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.bigquery.spi.BigQueryRpc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class OptionTest { + + private static final BigQueryRpc.Option RPC_OPTION = BigQueryRpc.Option.PAGE_TOKEN; + private static final BigQueryRpc.Option ANOTHER_RPC_OPTION = BigQueryRpc.Option.FIELDS; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final Option OPTION = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_EQUALS = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_NOT_EQUALS1 = new Option(RPC_OPTION, OTHER_VALUE) {}; + private static final Option OPTION_NOT_EQUALS2 = new Option(ANOTHER_RPC_OPTION, VALUE) {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(RPC_OPTION, OPTION.rpcOption()); + assertEquals(VALUE, OPTION.value()); + Option option = new Option(RPC_OPTION, null) {}; + assertEquals(RPC_OPTION, option.rpcOption()); + assertNull(option.value()); + thrown.expect(NullPointerException.class); + new Option(null, VALUE) {}; + } +} diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryJobConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryJobConfigurationTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryJobConfigurationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryJobConfigurationTest.java index 1ef270ee69cf..914a6db4d5eb 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryJobConfigurationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryJobConfigurationTest.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; +import com.google.cloud.bigquery.QueryJobConfiguration.Priority; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; -import com.google.gcloud.bigquery.QueryJobConfiguration.Priority; import org.junit.Test; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryRequestTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestTest.java similarity index 92% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryRequestTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestTest.java index 370b4d614cbf..f3682aa78457 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryRequestTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryRequestTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -29,13 +29,13 @@ public class QueryRequestTest { private static final DatasetId DATASET_ID = DatasetId.of("dataset"); private static final Boolean USE_QUERY_CACHE = true; private static final Boolean DRY_RUN = false; - private static final Long MAX_RESULTS = 42L; + private static final Long PAGE_SIZE = 42L; private static final Long MAX_WAIT_TIME = 42000L; private static final QueryRequest QUERY_REQUEST = QueryRequest.builder(QUERY) .useQueryCache(USE_QUERY_CACHE) .defaultDataset(DATASET_ID) .dryRun(DRY_RUN) - .maxResults(MAX_RESULTS) + .pageSize(PAGE_SIZE) .maxWaitTime(MAX_WAIT_TIME) .build(); @@ -65,7 +65,7 @@ public void testBuilder() { assertEquals(USE_QUERY_CACHE, QUERY_REQUEST.useQueryCache()); assertEquals(DATASET_ID, QUERY_REQUEST.defaultDataset()); assertEquals(DRY_RUN, QUERY_REQUEST.dryRun()); - assertEquals(MAX_RESULTS, QUERY_REQUEST.maxResults()); + assertEquals(PAGE_SIZE, QUERY_REQUEST.pageSize()); assertEquals(MAX_WAIT_TIME, QUERY_REQUEST.maxWaitTime()); thrown.expect(NullPointerException.class); QueryRequest.builder(null); @@ -78,7 +78,7 @@ public void testOf() { assertNull(request.useQueryCache()); assertNull(request.defaultDataset()); assertNull(request.dryRun()); - assertNull(request.maxResults()); + assertNull(request.pageSize()); assertNull(request.maxWaitTime()); thrown.expect(NullPointerException.class); QueryRequest.of(null); @@ -102,7 +102,7 @@ private void compareQueryRequest(QueryRequest expected, QueryRequest value) { assertEquals(expected.useQueryCache(), value.useQueryCache()); assertEquals(expected.defaultDataset(), value.defaultDataset()); assertEquals(expected.dryRun(), value.dryRun()); - assertEquals(expected.maxResults(), value.maxResults()); + assertEquals(expected.pageSize(), value.pageSize()); assertEquals(expected.maxWaitTime(), value.maxWaitTime()); } } diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResponseTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResponseTest.java similarity index 99% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResponseTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResponseTest.java index 08e885c8b3aa..8eae6a9e03e3 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResponseTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResponseTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResultTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResultTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResultTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResultTest.java index b6810ed93143..db4df54bfc0c 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryResultTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryResultTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryStageTest.java similarity index 95% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryStageTest.java index 99a7c8096454..670143190cf5 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/QueryStageTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import com.google.api.services.bigquery.model.ExplainQueryStep; +import com.google.cloud.bigquery.QueryStage.QueryStep; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.QueryStage.QueryStep; import org.junit.Test; @@ -48,7 +48,7 @@ public class QueryStageTest { private static final QueryStage QUERY_STAGE = QueryStage.builder() .computeRatioAvg(COMPUTE_RATIO_AVG) .computeRatioMax(COMPUTE_RATIO_MAX) - .id(ID) + .generatedId(ID) .name(NAME) .readRatioAvg(READ_RATIO_AVG) .readRatioMax(READ_RATIO_MAX) @@ -73,7 +73,7 @@ public void testQueryStepConstructor() { public void testBuilder() { assertEquals(COMPUTE_RATIO_AVG, QUERY_STAGE.computeRatioAvg(), 0); assertEquals(COMPUTE_RATIO_MAX, QUERY_STAGE.computeRatioMax(), 0); - assertEquals(ID, QUERY_STAGE.id()); + assertEquals(ID, QUERY_STAGE.generatedId()); assertEquals(NAME, QUERY_STAGE.name()); assertEquals(READ_RATIO_AVG, QUERY_STAGE.readRatioAvg(), 0); assertEquals(READ_RATIO_MAX, QUERY_STAGE.readRatioMax(), 0); @@ -108,7 +108,7 @@ private void compareQueryStage(QueryStage expected, QueryStage value) { assertEquals(expected, value); assertEquals(expected.computeRatioAvg(), value.computeRatioAvg(), 0); assertEquals(expected.computeRatioMax(), value.computeRatioMax(), 0); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.name(), value.name()); assertEquals(expected.readRatioAvg(), value.readRatioAvg(), 0); assertEquals(expected.readRatioMax(), value.readRatioMax(), 0); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SchemaTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SchemaTest.java index d24268d2e7cd..312b7e674991 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SchemaTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SchemaTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java similarity index 78% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java index d877bff2138c..30b1b9e067ec 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/SerializationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java @@ -14,32 +14,21 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; +package com.google.cloud.bigquery; +import com.google.cloud.AuthCredentials; +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.Restorable; +import com.google.cloud.bigquery.StandardTableDefinition.StreamingBuffer; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.AuthCredentials; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryParams; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.bigquery.StandardTableDefinition.StreamingBuffer; - -import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; -public class SerializationTest { +public class SerializationTest extends BaseSerializationTest { private static final Acl DOMAIN_ACCESS = Acl.of(new Acl.Domain("domain"), Acl.Role.WRITER); @@ -55,7 +44,7 @@ public class SerializationTest { private static final String DESCRIPTION = "Description"; private static final String ETAG = "0xFF00"; private static final String FRIENDLY_NAME = "friendlyDataset"; - private static final String ID = "P/D:1"; + private static final String GENERATED_ID = "P/D:1"; private static final Long LAST_MODIFIED = CREATION_TIME + 50; private static final String LOCATION = ""; private static final String SELF_LINK = "http://bigquery/p/d"; @@ -67,7 +56,7 @@ public class SerializationTest { .description(DESCRIPTION) .etag(ETAG) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModified(LAST_MODIFIED) .location(LOCATION) .selfLink(SELF_LINK) @@ -117,27 +106,28 @@ public class SerializationTest { .creationTime(CREATION_TIME) .description(DESCRIPTION) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .build(); private static final TableDefinition VIEW_DEFINITION = ViewDefinition.of("QUERY"); private static final TableInfo VIEW_INFO = TableInfo.builder(TABLE_ID, VIEW_DEFINITION) .creationTime(CREATION_TIME) .description(DESCRIPTION) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .build(); private static final TableInfo EXTERNAL_TABLE_INFO = TableInfo.builder(TABLE_ID, EXTERNAL_TABLE_DEFINITION) .creationTime(CREATION_TIME) .description(DESCRIPTION) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) + .build(); + private static final JobStatistics.CopyStatistics COPY_STATISTICS = + JobStatistics.CopyStatistics.builder() + .creationTime(1L) + .endTime(3L) + .startTime(2L) .build(); - private static final JobStatistics JOB_STATISTICS = JobStatistics.builder() - .creationTime(1L) - .endTime(3L) - .startTime(2L) - .build(); private static final JobStatistics.ExtractStatistics EXTRACT_STATISTICS = JobStatistics.ExtractStatistics.builder() .creationTime(1L) @@ -207,7 +197,7 @@ public class SerializationTest { .useQueryCache(true) .defaultDataset(DATASET_ID) .dryRun(false) - .maxResults(42L) + .pageSize(42L) .maxWaitTime(10L) .build(); private static final QueryResult QUERY_RESULT = QueryResult.builder() @@ -230,75 +220,40 @@ public class SerializationTest { new Dataset(BIGQUERY, new DatasetInfo.BuilderImpl(DATASET_INFO)); private static final Table TABLE = new Table(BIGQUERY, new TableInfo.BuilderImpl(TABLE_INFO)); private static final Job JOB = new Job(BIGQUERY, new JobInfo.BuilderImpl(JOB_INFO)); + private static final BigQueryException BIG_QUERY_EXCEPTION = + new BigQueryException(42, "message", BIGQUERY_ERROR); - @Test - public void testServiceOptions() throws Exception { + @Override + protected Serializable[] serializableObjects() { BigQueryOptions options = BigQueryOptions.builder() .projectId("p1") .authCredentials(AuthCredentials.createForAppEngine()) .build(); - BigQueryOptions serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - - options = options.toBuilder() + BigQueryOptions otherOptions = options.toBuilder() .projectId("p2") - .retryParams(RetryParams.defaultInstance()) .authCredentials(null) .build(); - serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - } - - @Test - public void testModelAndRequests() throws Exception { - Serializable[] objects = {DOMAIN_ACCESS, GROUP_ACCESS, USER_ACCESS, VIEW_ACCESS, DATASET_ID, + return new Serializable[]{DOMAIN_ACCESS, GROUP_ACCESS, USER_ACCESS, VIEW_ACCESS, DATASET_ID, DATASET_INFO, TABLE_ID, CSV_OPTIONS, STREAMING_BUFFER, TABLE_DEFINITION, EXTERNAL_TABLE_DEFINITION, VIEW_DEFINITION, TABLE_SCHEMA, TABLE_INFO, VIEW_INFO, - EXTERNAL_TABLE_INFO, INLINE_FUNCTION, URI_FUNCTION, JOB_STATISTICS, EXTRACT_STATISTICS, + EXTERNAL_TABLE_INFO, INLINE_FUNCTION, URI_FUNCTION, COPY_STATISTICS, EXTRACT_STATISTICS, LOAD_STATISTICS, QUERY_STATISTICS, BIGQUERY_ERROR, JOB_STATUS, JOB_ID, COPY_JOB_CONFIGURATION, EXTRACT_JOB_CONFIGURATION, LOAD_CONFIGURATION, LOAD_JOB_CONFIGURATION, QUERY_JOB_CONFIGURATION, JOB_INFO, INSERT_ALL_REQUEST, - INSERT_ALL_RESPONSE, FIELD_VALUE, QUERY_REQUEST, QUERY_RESPONSE, + INSERT_ALL_RESPONSE, FIELD_VALUE, QUERY_REQUEST, QUERY_RESPONSE, BIG_QUERY_EXCEPTION, BigQuery.DatasetOption.fields(), BigQuery.DatasetDeleteOption.deleteContents(), BigQuery.DatasetListOption.all(), BigQuery.TableOption.fields(), - BigQuery.TableListOption.maxResults(42L), BigQuery.JobOption.fields(), - BigQuery.JobListOption.allUsers(), DATASET, TABLE, JOB}; - for (Serializable obj : objects) { - Object copy = serializeAndDeserialize(obj); - assertEquals(obj, obj); - assertEquals(obj, copy); - assertNotSame(obj, copy); - assertEquals(copy, copy); - } + BigQuery.TableListOption.pageSize(42L), BigQuery.JobOption.fields(), + BigQuery.JobListOption.allUsers(), DATASET, TABLE, JOB, options, otherOptions}; } - @Test - public void testWriteChannelState() throws IOException, ClassNotFoundException { - BigQueryOptions options = BigQueryOptions.builder() - .projectId("p2") - .retryParams(RetryParams.defaultInstance()) - .build(); + @Override + protected Restorable[] restorableObjects() { + BigQueryOptions options = BigQueryOptions.builder().projectId("p2").build(); // avoid closing when you don't want partial writes upon failure @SuppressWarnings("resource") TableDataWriteChannel writer = new TableDataWriteChannel(options, LOAD_CONFIGURATION, "upload-id"); - RestorableState state = writer.capture(); - RestorableState deserializedState = serializeAndDeserialize(state); - assertEquals(state, deserializedState); - assertEquals(state.hashCode(), deserializedState.hashCode()); - assertEquals(state.toString(), deserializedState.toString()); - } - - @SuppressWarnings("unchecked") - private T serializeAndDeserialize(T obj) - throws IOException, ClassNotFoundException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { - output.writeObject(obj); - } - try (ObjectInputStream input = - new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { - return (T) input.readObject(); - } + return new Restorable[]{writer}; } } diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDataWriteChannelTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDataWriteChannelTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDataWriteChannelTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDataWriteChannelTest.java index 4c1be470ff57..646d82603950 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDataWriteChannelTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDataWriteChannelTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.capture; @@ -30,10 +30,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.gcloud.RestorableState; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.bigquery.spi.BigQueryRpc; -import com.google.gcloud.bigquery.spi.BigQueryRpcFactory; +import com.google.cloud.RestorableState; +import com.google.cloud.WriteChannel; +import com.google.cloud.bigquery.spi.BigQueryRpc; +import com.google.cloud.bigquery.spi.BigQueryRpcFactory; import org.easymock.Capture; import org.easymock.CaptureType; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDefinitionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDefinitionTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDefinitionTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDefinitionTest.java index d1e3635d00cb..7c7cf2568462 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableDefinitionTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableDefinitionTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import com.google.gcloud.bigquery.StandardTableDefinition.StreamingBuffer; +import com.google.cloud.bigquery.StandardTableDefinition.StreamingBuffer; import org.junit.Test; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableIdTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableIdTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableIdTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableIdTest.java index bc013bfa5c31..7db923bdd7ca 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableIdTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableIdTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableInfoTest.java similarity index 95% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableInfoTest.java index 18b8be10d71e..0b67f4be3adf 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableInfoTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableInfoTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; @@ -27,7 +27,7 @@ public class TableInfoTest { private static final String ETAG = "etag"; - private static final String ID = "project:dataset:table"; + private static final String GENERATED_ID = "project:dataset:table"; private static final String SELF_LINK = "selfLink"; private static final TableId TABLE_ID = TableId.of("dataset", "table"); private static final String FRIENDLY_NAME = "friendlyName"; @@ -89,7 +89,7 @@ public class TableInfoTest { .etag(ETAG) .expirationTime(EXPIRATION_TIME) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModifiedTime(LAST_MODIFIED_TIME) .selfLink(SELF_LINK) .build(); @@ -99,7 +99,7 @@ public class TableInfoTest { .etag(ETAG) .expirationTime(EXPIRATION_TIME) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModifiedTime(LAST_MODIFIED_TIME) .selfLink(SELF_LINK) .build(); @@ -110,7 +110,7 @@ public class TableInfoTest { .etag(ETAG) .expirationTime(EXPIRATION_TIME) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModifiedTime(LAST_MODIFIED_TIME) .selfLink(SELF_LINK) .build(); @@ -148,7 +148,7 @@ public void testBuilder() { assertEquals(ETAG, TABLE_INFO.etag()); assertEquals(EXPIRATION_TIME, TABLE_INFO.expirationTime()); assertEquals(FRIENDLY_NAME, TABLE_INFO.friendlyName()); - assertEquals(ID, TABLE_INFO.id()); + assertEquals(GENERATED_ID, TABLE_INFO.generatedId()); assertEquals(LAST_MODIFIED_TIME, TABLE_INFO.lastModifiedTime()); assertEquals(TABLE_DEFINITION, TABLE_INFO.definition()); assertEquals(SELF_LINK, TABLE_INFO.selfLink()); @@ -159,7 +159,7 @@ public void testBuilder() { assertEquals(ETAG, VIEW_INFO.etag()); assertEquals(EXPIRATION_TIME, VIEW_INFO.expirationTime()); assertEquals(FRIENDLY_NAME, VIEW_INFO.friendlyName()); - assertEquals(ID, VIEW_INFO.id()); + assertEquals(GENERATED_ID, VIEW_INFO.generatedId()); assertEquals(LAST_MODIFIED_TIME, VIEW_INFO.lastModifiedTime()); assertEquals(VIEW_TYPE, VIEW_INFO.definition()); assertEquals(SELF_LINK, VIEW_INFO.selfLink()); @@ -169,7 +169,7 @@ public void testBuilder() { assertEquals(ETAG, EXTERNAL_TABLE_INFO.etag()); assertEquals(EXPIRATION_TIME, EXTERNAL_TABLE_INFO.expirationTime()); assertEquals(FRIENDLY_NAME, EXTERNAL_TABLE_INFO.friendlyName()); - assertEquals(ID, EXTERNAL_TABLE_INFO.id()); + assertEquals(GENERATED_ID, EXTERNAL_TABLE_INFO.generatedId()); assertEquals(LAST_MODIFIED_TIME, EXTERNAL_TABLE_INFO.lastModifiedTime()); assertEquals(EXTERNAL_TABLE_DEFINITION, EXTERNAL_TABLE_INFO.definition()); assertEquals(SELF_LINK, EXTERNAL_TABLE_INFO.selfLink()); @@ -198,7 +198,7 @@ private void compareTableInfo(TableInfo expected, TableInfo value) { assertEquals(expected.etag(), value.etag()); assertEquals(expected.expirationTime(), value.expirationTime()); assertEquals(expected.friendlyName(), value.friendlyName()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.lastModifiedTime(), value.lastModifiedTime()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.definition(), value.definition()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableTest.java index 4866ee9ab8ec..c74fc8e244bc 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/TableTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/TableTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -28,12 +28,12 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.bigquery.InsertAllRequest.RowToInsert; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; -import com.google.gcloud.Page; -import com.google.gcloud.PageImpl; -import com.google.gcloud.bigquery.InsertAllRequest.RowToInsert; import org.junit.After; import org.junit.Test; @@ -44,7 +44,7 @@ public class TableTest { private static final String ETAG = "etag"; - private static final String ID = "project:dataset:table1"; + private static final String GENERATED_ID = "project:dataset:table1"; private static final String SELF_LINK = "selfLink"; private static final String FRIENDLY_NAME = "friendlyName"; private static final String DESCRIPTION = "description"; @@ -115,7 +115,7 @@ public void testBuilder() { .etag(ETAG) .expirationTime(EXPIRATION_TIME) .friendlyName(FRIENDLY_NAME) - .id(ID) + .generatedId(GENERATED_ID) .lastModifiedTime(LAST_MODIFIED_TIME) .selfLink(SELF_LINK) .build(); @@ -125,7 +125,7 @@ public void testBuilder() { assertEquals(ETAG, builtTable.etag()); assertEquals(EXPIRATION_TIME, builtTable.expirationTime()); assertEquals(FRIENDLY_NAME, builtTable.friendlyName()); - assertEquals(ID, builtTable.id()); + assertEquals(GENERATED_ID, builtTable.generatedId()); assertEquals(LAST_MODIFIED_TIME, builtTable.lastModifiedTime()); assertEquals(TABLE_DEFINITION, builtTable.definition()); assertEquals(SELF_LINK, builtTable.selfLink()); @@ -286,11 +286,11 @@ public void testListWithOptions() throws Exception { initializeExpectedTable(1); expect(bigquery.options()).andReturn(mockOptions); PageImpl> tableDataPage = new PageImpl<>(null, "c", ROWS); - expect(bigquery.listTableData(TABLE_ID1, BigQuery.TableDataListOption.maxResults(10L))) + expect(bigquery.listTableData(TABLE_ID1, BigQuery.TableDataListOption.pageSize(10L))) .andReturn(tableDataPage); replay(bigquery); initializeTable(); - Page> dataPage = table.list(BigQuery.TableDataListOption.maxResults(10L)); + Page> dataPage = table.list(BigQuery.TableDataListOption.pageSize(10L)); Iterator> tableDataIterator = tableDataPage.values().iterator(); Iterator> dataIterator = dataPage.values().iterator(); assertTrue(Iterators.elementsEqual(tableDataIterator, dataIterator)); @@ -398,7 +398,7 @@ private void compareTableInfo(TableInfo expected, TableInfo value) { assertEquals(expected.etag(), value.etag()); assertEquals(expected.expirationTime(), value.expirationTime()); assertEquals(expected.friendlyName(), value.friendlyName()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.lastModifiedTime(), value.lastModifiedTime()); assertEquals(expected.selfLink(), value.selfLink()); assertEquals(expected.definition(), value.definition()); diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/UserDefinedFunctionTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/UserDefinedFunctionTest.java index 2741aaed89a5..db6cada4e0e5 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/UserDefinedFunctionTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/UserDefinedFunctionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ViewDefinitionTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ViewDefinitionTest.java similarity index 98% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ViewDefinitionTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ViewDefinitionTest.java index ebab7a6e87ca..25c880bc8b78 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ViewDefinitionTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/ViewDefinitionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/WriteChannelConfigurationTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/WriteChannelConfigurationTest.java similarity index 97% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/WriteChannelConfigurationTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/WriteChannelConfigurationTest.java index dfde4795dacd..ffe1e58dc19e 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/WriteChannelConfigurationTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/WriteChannelConfigurationTest.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import com.google.cloud.bigquery.JobInfo.CreateDisposition; +import com.google.cloud.bigquery.JobInfo.WriteDisposition; import com.google.common.collect.ImmutableList; -import com.google.gcloud.bigquery.JobInfo.CreateDisposition; -import com.google.gcloud.bigquery.JobInfo.WriteDisposition; import org.junit.Test; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/it/ITBigQueryTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java similarity index 93% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/it/ITBigQueryTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 63a0551ece33..9e462a4d5142 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/it/ITBigQueryTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.bigquery.it; +package com.google.cloud.bigquery.it; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -23,54 +23,55 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.Page; +import com.google.cloud.WriteChannel; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQuery.DatasetField; +import com.google.cloud.bigquery.BigQuery.DatasetOption; +import com.google.cloud.bigquery.BigQuery.JobField; +import com.google.cloud.bigquery.BigQuery.JobListOption; +import com.google.cloud.bigquery.BigQuery.JobOption; +import com.google.cloud.bigquery.BigQuery.TableField; +import com.google.cloud.bigquery.BigQuery.TableOption; +import com.google.cloud.bigquery.BigQueryError; +import com.google.cloud.bigquery.BigQueryException; +import com.google.cloud.bigquery.CopyJobConfiguration; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.DatasetInfo; +import com.google.cloud.bigquery.ExternalTableDefinition; +import com.google.cloud.bigquery.ExtractJobConfiguration; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.FormatOptions; +import com.google.cloud.bigquery.InsertAllRequest; +import com.google.cloud.bigquery.InsertAllResponse; +import com.google.cloud.bigquery.Job; +import com.google.cloud.bigquery.JobInfo; +import com.google.cloud.bigquery.JobStatistics; +import com.google.cloud.bigquery.LoadJobConfiguration; +import com.google.cloud.bigquery.QueryJobConfiguration; +import com.google.cloud.bigquery.QueryRequest; +import com.google.cloud.bigquery.QueryResponse; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableDefinition; +import com.google.cloud.bigquery.TableId; +import com.google.cloud.bigquery.TableInfo; +import com.google.cloud.bigquery.ViewDefinition; +import com.google.cloud.bigquery.WriteChannelConfiguration; +import com.google.cloud.bigquery.testing.RemoteBigQueryHelper; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.testing.RemoteStorageHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.Page; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.bigquery.BigQuery; -import com.google.gcloud.bigquery.BigQuery.DatasetField; -import com.google.gcloud.bigquery.BigQuery.DatasetOption; -import com.google.gcloud.bigquery.BigQuery.JobField; -import com.google.gcloud.bigquery.BigQuery.JobListOption; -import com.google.gcloud.bigquery.BigQuery.JobOption; -import com.google.gcloud.bigquery.BigQuery.TableField; -import com.google.gcloud.bigquery.BigQuery.TableOption; -import com.google.gcloud.bigquery.BigQueryError; -import com.google.gcloud.bigquery.BigQueryException; -import com.google.gcloud.bigquery.CopyJobConfiguration; -import com.google.gcloud.bigquery.Dataset; -import com.google.gcloud.bigquery.DatasetId; -import com.google.gcloud.bigquery.DatasetInfo; -import com.google.gcloud.bigquery.ExternalTableDefinition; -import com.google.gcloud.bigquery.ExtractJobConfiguration; -import com.google.gcloud.bigquery.Field; -import com.google.gcloud.bigquery.FieldValue; -import com.google.gcloud.bigquery.FormatOptions; -import com.google.gcloud.bigquery.InsertAllRequest; -import com.google.gcloud.bigquery.InsertAllResponse; -import com.google.gcloud.bigquery.Job; -import com.google.gcloud.bigquery.JobInfo; -import com.google.gcloud.bigquery.JobStatistics; -import com.google.gcloud.bigquery.LoadJobConfiguration; -import com.google.gcloud.bigquery.QueryJobConfiguration; -import com.google.gcloud.bigquery.QueryRequest; -import com.google.gcloud.bigquery.QueryResponse; -import com.google.gcloud.bigquery.Schema; -import com.google.gcloud.bigquery.StandardTableDefinition; -import com.google.gcloud.bigquery.Table; -import com.google.gcloud.bigquery.TableDefinition; -import com.google.gcloud.bigquery.TableId; -import com.google.gcloud.bigquery.TableInfo; -import com.google.gcloud.bigquery.ViewDefinition; -import com.google.gcloud.bigquery.WriteChannelConfiguration; -import com.google.gcloud.bigquery.testing.RemoteBigQueryHelper; -import com.google.gcloud.storage.BlobInfo; -import com.google.gcloud.storage.BucketInfo; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.testing.RemoteGcsHelper; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; @@ -134,7 +135,7 @@ public class ITBigQueryTest { private static final String LOAD_FILE = "load.csv"; private static final String JSON_LOAD_FILE = "load.json"; private static final String EXTRACT_FILE = "extract.csv"; - private static final String BUCKET = RemoteGcsHelper.generateBucketName(); + private static final String BUCKET = RemoteStorageHelper.generateBucketName(); private static final TableId TABLE_ID = TableId.of(DATASET, "testing_table"); private static final String CSV_CONTENT = "StringValue1\nStringValue2\n"; private static final String JSON_CONTENT = "{" @@ -171,9 +172,9 @@ public class ITBigQueryTest { @BeforeClass public static void beforeClass() throws InterruptedException { RemoteBigQueryHelper bigqueryHelper = RemoteBigQueryHelper.create(); - RemoteGcsHelper gcsHelper = RemoteGcsHelper.create(); + RemoteStorageHelper storageHelper = RemoteStorageHelper.create(); bigquery = bigqueryHelper.options().service(); - storage = gcsHelper.options().service(); + storage = storageHelper.options().service(); storage.create(BucketInfo.of(BUCKET)); storage.create(BlobInfo.builder(BUCKET, LOAD_FILE).contentType("text/plain").build(), CSV_CONTENT.getBytes(StandardCharsets.UTF_8)); @@ -199,7 +200,7 @@ public static void afterClass() throws ExecutionException, InterruptedException RemoteBigQueryHelper.forceDelete(bigquery, DATASET); } if (storage != null) { - boolean wasDeleted = RemoteGcsHelper.forceDelete(storage, BUCKET, 10, TimeUnit.SECONDS); + boolean wasDeleted = RemoteStorageHelper.forceDelete(storage, BUCKET, 10, TimeUnit.SECONDS); if (!wasDeleted && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); } @@ -214,7 +215,7 @@ public void testGetDataset() { assertEquals(DESCRIPTION, dataset.description()); assertNotNull(dataset.acl()); assertNotNull(dataset.etag()); - assertNotNull(dataset.id()); + assertNotNull(dataset.generatedId()); assertNotNull(dataset.lastModified()); assertNotNull(dataset.selfLink()); } @@ -231,7 +232,7 @@ public void testGetDatasetWithSelectedFields() { assertNull(dataset.acl()); assertNull(dataset.etag()); assertNull(dataset.friendlyName()); - assertNull(dataset.id()); + assertNull(dataset.generatedId()); assertNull(dataset.lastModified()); assertNull(dataset.location()); assertNull(dataset.selfLink()); @@ -270,7 +271,7 @@ public void testUpdateDatasetWithSelectedFields() { assertNull(updatedDataset.acl()); assertNull(updatedDataset.etag()); assertNull(updatedDataset.friendlyName()); - assertNull(updatedDataset.id()); + assertNull(updatedDataset.generatedId()); assertNull(updatedDataset.lastModified()); assertNull(updatedDataset.location()); assertNull(updatedDataset.selfLink()); @@ -348,7 +349,7 @@ public void testCreateExternalTable() throws InterruptedException { + tableName) .defaultDataset(DatasetId.of(DATASET)) .maxWaitTime(60000L) - .maxResults(1000L) + .pageSize(1000L) .build(); QueryResponse response = bigquery.query(request); while (!response.jobCompleted()) { @@ -411,7 +412,7 @@ public void testCreateViewTable() throws InterruptedException { QueryRequest request = QueryRequest.builder("SELECT * FROM " + tableName) .defaultDataset(DatasetId.of(DATASET)) .maxWaitTime(60000L) - .maxResults(1000L) + .pageSize(1000L) .build(); QueryResponse response = bigquery.query(request); while (!response.jobCompleted()) { @@ -623,6 +624,7 @@ public void testInsertAllWithErrors() { assertTrue(bigquery.delete(TableId.of(DATASET, tableName))); } + @Ignore("Flaky test; see issue #836") @Test public void testListAllTableData() { Page> rows = bigquery.listTableData(TABLE_ID); @@ -662,7 +664,7 @@ public void testQuery() throws InterruptedException { QueryRequest request = QueryRequest.builder(query) .defaultDataset(DatasetId.of(DATASET)) .maxWaitTime(60000L) - .maxResults(1000L) + .pageSize(1000L) .build(); QueryResponse response = bigquery.query(request); while (!response.jobCompleted()) { @@ -697,7 +699,7 @@ public void testListJobs() { assertNotNull(job.statistics()); assertNotNull(job.status()); assertNotNull(job.userEmail()); - assertNotNull(job.id()); + assertNotNull(job.generatedId()); } } @@ -709,7 +711,7 @@ public void testListJobsWithSelectedFields() { assertNotNull(job.status()); assertNotNull(job.userEmail()); assertNull(job.statistics()); - assertNull(job.id()); + assertNull(job.generatedId()); } } @@ -906,6 +908,7 @@ public void testCancelNonExistingJob() { assertFalse(bigquery.cancel("test_cancel_non_existing_job")); } + @Ignore("Flaky test; see #836") @Test public void testInsertFromFile() throws InterruptedException { String destinationTableName = "test_insert_from_file_table"; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/RemoteBigQueryHelperTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java similarity index 95% rename from gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/RemoteBigQueryHelperTest.java rename to gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java index 267ae161b7aa..46badf7bb7be 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/RemoteBigQueryHelperTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/testing/RemoteBigQueryHelperTest.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.bigquery; +package com.google.cloud.bigquery.testing; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import com.google.gcloud.bigquery.BigQuery.DatasetDeleteOption; -import com.google.gcloud.bigquery.testing.RemoteBigQueryHelper; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQuery.DatasetDeleteOption; +import com.google.cloud.bigquery.BigQueryOptions; import org.easymock.EasyMock; import org.junit.Rule; diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/OptionTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/OptionTest.java deleted file mode 100644 index 2c89ececedb8..000000000000 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/OptionTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.bigquery; - -import static org.junit.Assert.assertEquals; - -import com.google.gcloud.bigquery.spi.BigQueryRpc; - -import org.junit.Test; - -public class OptionTest { - - @Test - public void testOption() { - Option option = new Option(BigQueryRpc.Option.PAGE_TOKEN, "token"); - assertEquals(BigQueryRpc.Option.PAGE_TOKEN, option.rpcOption()); - assertEquals("token", option.value()); - } - - @Test(expected = NullPointerException.class) - public void testNullRpcOption() { - new Option(null, "token"); - } -} diff --git a/gcloud-java-contrib/pom.xml b/gcloud-java-contrib/pom.xml index e1370fb0ec8e..0c1f675703b1 100644 --- a/gcloud-java-contrib/pom.xml +++ b/gcloud-java-contrib/pom.xml @@ -4,13 +4,14 @@ gcloud-java-contrib pom GCloud Java contributions + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-contrib Contains packages that provide higher-level abstraction/functionality for common gcloud-java use cases. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-contrib diff --git a/gcloud-java-core/README.md b/gcloud-java-core/README.md index fc5f481f8ec3..9a659c0f8cc0 100644 --- a/gcloud-java-core/README.md +++ b/gcloud-java-core/README.md @@ -5,30 +5,30 @@ This module provides common functionality required by service-specific modules o [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-core.svg)](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-core.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-core.svg)](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-core.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/package-summary.html) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/package-summary.html) Quickstart ---------- If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-core - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-core:0.1.5' +compile 'com.google.cloud:gcloud-java-core:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-core" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-core" % "0.2.0" ``` Troubleshooting diff --git a/gcloud-java-core/pom.xml b/gcloud-java-core/pom.xml index 6d0ed675b423..22648d2e6ffa 100644 --- a/gcloud-java-core/pom.xml +++ b/gcloud-java-core/pom.xml @@ -4,13 +4,14 @@ gcloud-java-core jar GCloud Java core + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-core Core module for the gcloud-java. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-core diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java b/gcloud-java-core/src/main/java/com/google/cloud/AuthCredentials.java similarity index 73% rename from gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java rename to gcloud-java-core/src/main/java/com/google/cloud/AuthCredentials.java index 6f9e09ca04bc..ec5a631f5f54 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/AuthCredentials.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkNotNull; @@ -25,9 +25,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; import java.util.Collection; +import java.util.Date; import java.util.Objects; /** @@ -35,16 +41,27 @@ */ public abstract class AuthCredentials implements Restorable { - private static class AppEngineAuthCredentials extends AuthCredentials { + /** + * Represents built-in credentials when running in Google App Engine. + */ + public static class AppEngineAuthCredentials extends AuthCredentials + implements ServiceAccountSigner { private static final AuthCredentials INSTANCE = new AppEngineAuthCredentials(); private static final AppEngineAuthCredentialsState STATE = new AppEngineAuthCredentialsState(); - private static class AppEngineCredentials extends GoogleCredentials { + private AppEngineCredentials credentials; + + private static class AppEngineCredentials extends GoogleCredentials + implements ServiceAccountSigner { private final Object appIdentityService; + private final String account; private final Method getAccessToken; private final Method getAccessTokenResult; + private final Method getExpirationTime; + private final Method signForApp; + private final Method getSignature; private final Collection scopes; AppEngineCredentials() { @@ -59,6 +76,13 @@ private static class AppEngineCredentials extends GoogleCredentials { "com.google.appengine.api.appidentity.AppIdentityService$GetAccessTokenResult"); this.getAccessTokenResult = serviceClass.getMethod("getAccessToken", Iterable.class); this.getAccessToken = tokenResultClass.getMethod("getAccessToken"); + this.getExpirationTime = tokenResultClass.getMethod("getExpirationTime"); + this.account = (String) serviceClass.getMethod("getServiceAccountName") + .invoke(appIdentityService); + this.signForApp = serviceClass.getMethod("signForApp", byte[].class); + Class signingResultClass = Class.forName( + "com.google.appengine.api.appidentity.AppIdentityService$SigningResult"); + this.getSignature = signingResultClass.getMethod("getSignature"); this.scopes = null; } catch (Exception e) { throw new RuntimeException("Could not create AppEngineCredentials.", e); @@ -69,11 +93,15 @@ private static class AppEngineCredentials extends GoogleCredentials { this.appIdentityService = unscoped.appIdentityService; this.getAccessToken = unscoped.getAccessToken; this.getAccessTokenResult = unscoped.getAccessTokenResult; + this.getExpirationTime = unscoped.getExpirationTime; + this.account = unscoped.account; + this.signForApp = unscoped.signForApp; + this.getSignature = unscoped.getSignature; this.scopes = scopes; } /** - * Refresh the access token by getting it from the App Identity service + * Refresh the access token by getting it from the App Identity service. */ @Override public AccessToken refreshAccessToken() throws IOException { @@ -83,7 +111,8 @@ public AccessToken refreshAccessToken() throws IOException { try { Object accessTokenResult = getAccessTokenResult.invoke(appIdentityService, scopes); String accessToken = (String) getAccessToken.invoke(accessTokenResult); - return new AccessToken(accessToken, null); + Date expirationTime = (Date) getExpirationTime.invoke(accessTokenResult); + return new AccessToken(accessToken, expirationTime); } catch (Exception e) { throw new IOException("Could not get the access token.", e); } @@ -98,6 +127,21 @@ public boolean createScopedRequired() { public GoogleCredentials createScoped(Collection scopes) { return new AppEngineCredentials(scopes, this); } + + @Override + public String account() { + return account; + } + + @Override + public byte[] sign(byte[] toSign) { + try { + Object signingResult = signForApp.invoke(appIdentityService, toSign); + return (byte[]) getSignature.invoke(signingResult); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new SigningException("Failed to sign the provided bytes", ex); + } + } } private static class AppEngineAuthCredentialsState @@ -122,14 +166,27 @@ public boolean equals(Object obj) { } @Override - public GoogleCredentials credentials() { - return new AppEngineCredentials(); + public AppEngineCredentials credentials() { + if (credentials == null) { + credentials = new AppEngineCredentials(); + } + return credentials; } @Override public RestorableState capture() { return STATE; } + + @Override + public String account() { + return credentials().account(); + } + + @Override + public byte[] sign(byte[] toSign) { + return credentials().sign(toSign); + } } /** @@ -138,8 +195,10 @@ public RestorableState capture() { * @see * User accounts and service accounts */ - public static class ServiceAccountAuthCredentials extends AuthCredentials { + public static class ServiceAccountAuthCredentials extends AuthCredentials + implements ServiceAccountSigner { + private final ServiceAccountCredentials credentials; private final String account; private final PrivateKey privateKey; @@ -178,23 +237,44 @@ public boolean equals(Object obj) { } ServiceAccountAuthCredentials(String account, PrivateKey privateKey) { - this.account = checkNotNull(account); - this.privateKey = checkNotNull(privateKey); + this(new ServiceAccountCredentials(null, account, privateKey, null, null)); + } + + ServiceAccountAuthCredentials(ServiceAccountCredentials credentials) { + this.credentials = checkNotNull(credentials); + this.account = checkNotNull(credentials.getClientEmail()); + this.privateKey = checkNotNull(credentials.getPrivateKey()); } @Override public ServiceAccountCredentials credentials() { - return new ServiceAccountCredentials(null, account, privateKey, null, null); + return credentials; } + @Override public String account() { return account; } + /** + * Returns the private key associated with the service account credentials. + */ public PrivateKey privateKey() { return privateKey; } + @Override + public byte[] sign(byte[] toSign) { + try { + Signature signer = Signature.getInstance("SHA256withRSA"); + signer.initSign(privateKey()); + signer.update(toSign); + return signer.sign(); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) { + throw new SigningException("Failed to sign the provided bytes", ex); + } + } + @Override public RestorableState capture() { return new ServiceAccountAuthCredentialsState(account, privateKey); @@ -242,6 +322,10 @@ public boolean equals(Object obj) { } } + ApplicationDefaultAuthCredentials(GoogleCredentials credentials) { + googleCredentials = credentials; + } + ApplicationDefaultAuthCredentials() throws IOException { googleCredentials = GoogleCredentials.getApplicationDefault(); } @@ -260,7 +344,7 @@ public RestorableState capture() { /** * A placeholder for credentials to signify that requests sent to the server should not be * authenticated. This is typically useful when using the local service emulators, such as - * {@code LocalGcdHelper} and {@code LocalResourceManagerHelper}. + * {@code LocalDatastoreHelper} and {@code LocalResourceManagerHelper}. */ public static class NoAuthCredentials extends AuthCredentials { @@ -276,8 +360,20 @@ private static class NoAuthCredentialsState public AuthCredentials restore() { return INSTANCE; } + + @Override + public int hashCode() { + return getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof NoAuthCredentialsState; + } } + private NoAuthCredentials() {} + @Override public GoogleCredentials credentials() { return null; @@ -308,7 +404,12 @@ public static AuthCredentials createForAppEngine() { * @throws IOException if the credentials cannot be created in the current environment */ public static AuthCredentials createApplicationDefaults() throws IOException { - return new ApplicationDefaultAuthCredentials(); + GoogleCredentials credentials = GoogleCredentials.getApplicationDefault(); + if (credentials instanceof ServiceAccountCredentials) { + ServiceAccountCredentials serviceAccountCredentials = (ServiceAccountCredentials) credentials; + return new ServiceAccountAuthCredentials(serviceAccountCredentials); + } + return new ApplicationDefaultAuthCredentials(credentials); } /** @@ -329,7 +430,7 @@ public static ServiceAccountAuthCredentials createFor(String account, PrivateKey /** * Creates a placeholder denoting that no credentials should be used. This is typically useful - * when using the local service emulators, such as {@code LocalGcdHelper} and + * when using the local service emulators, such as {@code LocalDatastoreHelper} and * {@code LocalResourceManagerHelper}. */ public static AuthCredentials noAuth() { diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/BaseService.java b/gcloud-java-core/src/main/java/com/google/cloud/BaseService.java similarity index 95% rename from gcloud-java-core/src/main/java/com/google/gcloud/BaseService.java rename to gcloud-java-core/src/main/java/com/google/cloud/BaseService.java index d9e6f2db7c95..d0476b66c29c 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/BaseService.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/BaseService.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; -import com.google.gcloud.ExceptionHandler.Interceptor; +import com.google.cloud.ExceptionHandler.Interceptor; /** * Base class for service objects. diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java b/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java similarity index 69% rename from gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java rename to gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java index 365243904436..ad2d6bf07144 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/BaseServiceException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; @@ -32,16 +32,32 @@ */ public class BaseServiceException extends RuntimeException { + private static final long serialVersionUID = 759921776378760835L; + public static final int UNKNOWN_CODE = 0; + + private final int code; + private final boolean retryable; + private final String reason; + private final boolean idempotent; + private final String location; + private final String debugInfo; + protected static final class Error implements Serializable { private static final long serialVersionUID = -4019600198652965721L; private final Integer code; private final String reason; + private final boolean rejected; public Error(Integer code, String reason) { + this(code, reason, false); + } + + public Error(Integer code, String reason, boolean rejected) { this.code = code; this.reason = reason; + this.rejected = rejected; } /** @@ -51,6 +67,15 @@ public Integer code() { return code; } + /** + * Returns true if the error indicates that the API call was certainly not accepted by the + * server. For instance, if the server returns a rate limit exceeded error, it certainly did not + * process the request and this method will return {@code true}. + */ + public boolean rejected() { + return rejected; + } + /** * Returns the reason that caused the exception. */ @@ -58,11 +83,11 @@ public String reason() { return reason; } - boolean isRetryable(Set retryableErrors) { + boolean isRetryable(boolean idempotent, Set retryableErrors) { for (Error retryableError : retryableErrors) { if ((retryableError.code() == null || retryableError.code().equals(this.code())) && (retryableError.reason() == null || retryableError.reason().equals(this.reason()))) { - return true; + return idempotent || retryableError.rejected(); } } return false; @@ -79,28 +104,20 @@ public int hashCode() { } } - private static final long serialVersionUID = 759921776378760835L; - public static final int UNKNOWN_CODE = 0; - - private final int code; - private final boolean retryable; - private final String reason; - private final boolean idempotent; - private final String location; - private final String debugInfo; - public BaseServiceException(IOException exception, boolean idempotent) { super(message(exception), exception); int code = UNKNOWN_CODE; String reason = null; String location = null; String debugInfo = null; + Boolean retryable = null; if (exception instanceof GoogleJsonResponseException) { GoogleJsonError jsonError = ((GoogleJsonResponseException) exception).getDetails(); if (jsonError != null) { - Error error = error(jsonError); + Error error = new Error(jsonError.getCode(), reason(jsonError)); code = error.code; reason = error.reason; + retryable = isRetryable(idempotent, error); if (reason != null) { GoogleJsonError.ErrorInfo errorInfo = jsonError.getErrors().get(0); location = errorInfo.getLocation(); @@ -110,8 +127,8 @@ public BaseServiceException(IOException exception, boolean idempotent) { code = ((GoogleJsonResponseException) exception).getStatusCode(); } } + this.retryable = MoreObjects.firstNonNull(retryable, isRetryable(idempotent, exception)); this.code = code; - this.retryable = idempotent && isRetryable(exception); this.reason = reason; this.idempotent = idempotent; this.location = location; @@ -119,13 +136,7 @@ public BaseServiceException(IOException exception, boolean idempotent) { } public BaseServiceException(GoogleJsonError error, boolean idempotent) { - super(error.getMessage()); - this.code = error.getCode(); - this.reason = reason(error); - this.idempotent = idempotent; - this.retryable = idempotent && isRetryable(error); - this.location = null; - this.debugInfo = null; + this(error.getCode(), error.getMessage(), reason(error), idempotent); } public BaseServiceException(int code, String message, String reason, boolean idempotent) { @@ -138,7 +149,7 @@ public BaseServiceException(int code, String message, String reason, boolean ide this.code = code; this.reason = reason; this.idempotent = idempotent; - this.retryable = idempotent && new Error(code, reason).isRetryable(retryableErrors()); + this.retryable = isRetryable(idempotent, new Error(code, reason)); this.location = null; this.debugInfo = null; } @@ -147,15 +158,12 @@ protected Set retryableErrors() { return Collections.emptySet(); } - protected boolean isRetryable(GoogleJsonError error) { - return error != null && error(error).isRetryable(retryableErrors()); + protected boolean isRetryable(boolean idempotent, Error error) { + return error.isRetryable(idempotent, retryableErrors()); } - protected boolean isRetryable(IOException exception) { - if (exception instanceof GoogleJsonResponseException) { - return isRetryable(((GoogleJsonResponseException) exception).getDetails()); - } - return exception instanceof SocketTimeoutException; + protected boolean isRetryable(boolean idempotent, IOException exception) { + return idempotent && exception instanceof SocketTimeoutException; } /** @@ -187,8 +195,8 @@ public boolean idempotent() { } /** - * Returns the service location where the error causing the exception occurred. Returns - * {@code null} if not set. + * Returns the service location where the error causing the exception occurred. Returns {@code + * null} if not available. */ public String location() { return location; @@ -198,18 +206,39 @@ protected String debugInfo() { return debugInfo; } - protected static String reason(GoogleJsonError error) { - if (error.getErrors() != null && !error.getErrors().isEmpty()) { - return error.getErrors().get(0).getReason(); + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; } - return null; + if (!(obj instanceof BaseServiceException)) { + return false; + } + BaseServiceException other = (BaseServiceException) obj; + return Objects.equals(getCause(), other.getCause()) + && Objects.equals(getMessage(), other.getMessage()) + && code == other.code + && retryable == other.retryable + && Objects.equals(reason, other.reason) + && idempotent == other.idempotent + && Objects.equals(location, other.location) + && Objects.equals(debugInfo, other.debugInfo); } - protected static Error error(GoogleJsonError error) { - return new Error(error.getCode(), reason(error)); + @Override + public int hashCode() { + return Objects.hash(getCause(), getMessage(), code, retryable, reason, idempotent, location, + debugInfo); + } + + private static String reason(GoogleJsonError error) { + if (error.getErrors() != null && !error.getErrors().isEmpty()) { + return error.getErrors().get(0).getReason(); + } + return null; } - protected static String message(IOException exception) { + private static String message(IOException exception) { if (exception instanceof GoogleJsonResponseException) { GoogleJsonError details = ((GoogleJsonResponseException) exception).getDetails(); if (details != null) { diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/BaseWriteChannel.java b/gcloud-java-core/src/main/java/com/google/cloud/BaseWriteChannel.java similarity index 99% rename from gcloud-java-core/src/main/java/com/google/gcloud/BaseWriteChannel.java rename to gcloud-java-core/src/main/java/com/google/cloud/BaseWriteChannel.java index 1d18a5a27e81..038ef8c362de 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/BaseWriteChannel.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/BaseWriteChannel.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; +import com.google.cloud.BaseWriteChannel.BaseState; import com.google.common.base.MoreObjects; import java.io.IOException; diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java b/gcloud-java-core/src/main/java/com/google/cloud/ExceptionHandler.java similarity index 93% rename from gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java rename to gcloud-java-core/src/main/java/com/google/cloud/ExceptionHandler.java index 39d4c4e75a1a..e72eb1edcf88 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ExceptionHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkNotNull; @@ -26,6 +26,7 @@ import java.io.Serializable; import java.lang.reflect.Method; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Callable; @@ -259,6 +260,26 @@ boolean shouldRetry(Exception ex) { return retryResult == Interceptor.RetryResult.RETRY; } + @Override + public int hashCode() { + return Objects.hash(interceptors, retriableExceptions, nonRetriableExceptions, retryInfo); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ExceptionHandler)) { + return false; + } + ExceptionHandler other = (ExceptionHandler) obj; + return Objects.equals(interceptors, other.interceptors) + && Objects.equals(retriableExceptions, other.retriableExceptions) + && Objects.equals(nonRetriableExceptions, other.nonRetriableExceptions) + && Objects.equals(retryInfo, other.retryInfo); + } + /** * Returns an instance which retry any checked exception and abort on any runtime exception. */ 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 new file mode 100644 index 000000000000..5a46d058648d --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/cloud/FieldSelector.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016 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.cloud; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +/** + * Interface for Google Cloud resource's fields. Implementations of this interface can be used to + * select only desired fields from a returned Google Cloud resource. + */ +public interface FieldSelector { + + /** + * Returns a string selector. This selector is passed to a Google Cloud service (possibly with + * other field selectors) to specify which resource fields should be returned by an API call. + */ + String selector(); + + /** + * A helper class used to build composite selectors given a number of fields. This class is not + * supposed to be used directly by users. + */ + class Helper { + + private static final String[] EMPTY_FIELDS = {}; + + private Helper() {} + + private static final Function FIELD_TO_STRING_FUNCTION = + new Function() { + @Override + public String apply(FieldSelector fieldSelector) { + return fieldSelector.selector(); + } + }; + + private static String selector(List required, FieldSelector[] others, + String... extraResourceFields) { + Set fieldStrings = Sets.newHashSetWithExpectedSize(required.size() + others.length); + fieldStrings.addAll(Lists.transform(required, FIELD_TO_STRING_FUNCTION)); + fieldStrings.addAll(Lists.transform(Arrays.asList(others), FIELD_TO_STRING_FUNCTION)); + fieldStrings.addAll(Arrays.asList(extraResourceFields)); + return Joiner.on(',').join(fieldStrings); + } + + /** + * 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 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) { + return "nextPageToken," + containerName + '(' + selector(required, others) + ')'; + } + + /** + * Returns a composite selector given 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 containerName, List required, + FieldSelector[] others, String... extraResourceFields) { + 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/main/java/com/google/gcloud/IamPolicy.java b/gcloud-java-core/src/main/java/com/google/cloud/IamPolicy.java similarity index 69% rename from gcloud-java-core/src/main/java/com/google/gcloud/IamPolicy.java rename to gcloud-java-core/src/main/java/com/google/cloud/IamPolicy.java index 748eaba2ab4c..2d15d9907687 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/IamPolicy.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/IamPolicy.java @@ -14,20 +14,19 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.Serializable; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -69,12 +68,16 @@ protected Builder() {} /** * Replaces the builder's map of bindings with the given map of bindings. * - * @throws IllegalArgumentException if the provided map is null or contain any null values + * @throws NullPointerException if the given map is null or contains any null keys or values + * @throws IllegalArgumentException if any identities in the given map are null */ public final B bindings(Map> bindings) { - checkArgument(bindings != null, "The provided map of bindings cannot be null."); + checkNotNull(bindings, "The provided map of bindings cannot be null."); for (Map.Entry> binding : bindings.entrySet()) { - verifyBinding(binding.getKey(), binding.getValue()); + checkNotNull(binding.getKey(), "The role cannot be null."); + Set identities = binding.getValue(); + checkNotNull(identities, "A role cannot be assigned to a null set of identities."); + checkArgument(!identities.contains(null), "Null identities are not permitted."); } this.bindings.clear(); for (Map.Entry> binding : bindings.entrySet()) { @@ -84,78 +87,50 @@ public final B bindings(Map> bindings) { } /** - * Adds a binding to the policy. - * - * @throws IllegalArgumentException if the policy already contains a binding with the same role - * or if the role or any identities are null + * Removes the role (and all identities associated with that role) from the policy. */ - public final B addBinding(R role, Set identities) { - verifyBinding(role, identities); - checkArgument(!bindings.containsKey(role), - "The policy already contains a binding with the role " + role.toString() + "."); - bindings.put(role, new HashSet(identities)); - return self(); - } - - /** - * Adds a binding to the policy. - * - * @throws IllegalArgumentException if the policy already contains a binding with the same role - * or if the role or any identities are null - */ - public final B addBinding(R role, Identity first, Identity... others) { - HashSet identities = new HashSet<>(); - identities.add(first); - identities.addAll(Arrays.asList(others)); - return addBinding(role, identities); - } - - private void verifyBinding(R role, Collection identities) { - checkArgument(role != null, "The role cannot be null."); - verifyIdentities(identities); - } - - private void verifyIdentities(Collection identities) { - checkArgument(identities != null, "A role cannot be assigned to a null set of identities."); - checkArgument(!identities.contains(null), "Null identities are not permitted."); - } - - /** - * Removes the binding associated with the specified role. - */ - public final B removeBinding(R role) { + public final B removeRole(R role) { bindings.remove(role); return self(); } /** - * Adds one or more identities to an existing binding. + * Adds one or more identities to the policy under the role specified. * - * @throws IllegalArgumentException if the policy doesn't contain a binding with the specified - * role or any identities are null + * @throws NullPointerException if the role or any of the identities is null. */ public final B addIdentity(R role, Identity first, Identity... others) { - checkArgument(bindings.containsKey(role), - "The policy doesn't contain the role " + role.toString() + "."); - List toAdd = new LinkedList<>(); + String nullIdentityMessage = "Null identities are not permitted."; + checkNotNull(first, nullIdentityMessage); + checkNotNull(others, nullIdentityMessage); + for (Identity identity : others) { + checkNotNull(identity, nullIdentityMessage); + } + Set toAdd = new LinkedHashSet<>(); toAdd.add(first); toAdd.addAll(Arrays.asList(others)); - verifyIdentities(toAdd); - bindings.get(role).addAll(toAdd); + Set identities = bindings.get(checkNotNull(role, "The role cannot be null.")); + if (identities == null) { + identities = new HashSet(); + bindings.put(role, identities); + } + identities.addAll(toAdd); return self(); } /** - * Removes one or more identities from an existing binding. - * - * @throws IllegalArgumentException if the policy doesn't contain a binding with the specified - * role + * Removes one or more identities from an existing binding. Does nothing if the binding + * associated with the provided role doesn't exist. */ public final B removeIdentity(R role, Identity first, Identity... others) { - checkArgument(bindings.containsKey(role), - "The policy doesn't contain the role " + role.toString() + "."); - bindings.get(role).remove(first); - bindings.get(role).removeAll(Arrays.asList(others)); + Set identities = bindings.get(role); + if (identities != null) { + identities.remove(first); + identities.removeAll(Arrays.asList(others)); + } + if (identities != null && identities.isEmpty()) { + bindings.remove(role); + } return self(); } @@ -244,7 +219,10 @@ public final int hashCode() { @Override public final boolean equals(Object obj) { - if (obj == null || !getClass().equals(obj.getClass())) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(getClass())) { return false; } @SuppressWarnings("rawtypes") diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Identity.java b/gcloud-java-core/src/main/java/com/google/cloud/Identity.java similarity index 91% rename from gcloud-java-core/src/main/java/com/google/gcloud/Identity.java rename to gcloud-java-core/src/main/java/com/google/cloud/Identity.java index d1644198f759..fab70dfe6d37 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/Identity.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/Identity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkNotNull; @@ -44,7 +44,7 @@ public final class Identity implements Serializable { private static final long serialVersionUID = -8181841964597657446L; private final Type type; - private final String id; + private final String value; /** * The types of IAM identities. @@ -82,9 +82,9 @@ public enum Type { DOMAIN } - private Identity(Type type, String id) { + private Identity(Type type, String value) { this.type = type; - this.id = id; + this.value = value; } public Type type() { @@ -92,7 +92,7 @@ public Type type() { } /** - * Returns the string identifier for this identity. The id corresponds to: + * Returns the string identifier for this identity. The value corresponds to: *
    *
  • email address (for identities of type {@code USER}, {@code SERVICE_ACCOUNT}, and * {@code GROUP}) @@ -101,8 +101,8 @@ public Type type() { * {@code ALL_AUTHENTICATED_USERS}) *
*/ - public String id() { - return id; + public String value() { + return value; } /** @@ -163,7 +163,7 @@ public static Identity domain(String domain) { @Override public int hashCode() { - return Objects.hash(id, type); + return Objects.hash(value, type); } @Override @@ -172,7 +172,7 @@ public boolean equals(Object obj) { return false; } Identity other = (Identity) obj; - return Objects.equals(id, other.id()) && Objects.equals(type, other.type()); + return Objects.equals(value, other.value()) && Objects.equals(type, other.type()); } /** @@ -186,13 +186,13 @@ public String strValue() { case ALL_AUTHENTICATED_USERS: return "allAuthenticatedUsers"; case USER: - return "user:" + id; + return "user:" + value; case SERVICE_ACCOUNT: - return "serviceAccount:" + id; + return "serviceAccount:" + value; case GROUP: - return "group:" + id; + return "group:" + value; case DOMAIN: - return "domain:" + id; + return "domain:" + value; default: throw new IllegalStateException("Unexpected identity type: " + type); } diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Page.java b/gcloud-java-core/src/main/java/com/google/cloud/Page.java similarity index 92% rename from gcloud-java-core/src/main/java/com/google/gcloud/Page.java rename to gcloud-java-core/src/main/java/com/google/cloud/Page.java index 53f3a3842a18..8881a98b46ae 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/Page.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/Page.java @@ -14,15 +14,14 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import java.util.Iterator; /** * Interface for Google Cloud paginated results. * - *

- * Use {@code Page} to iterate through all values (also in next pages): + *

Use {@code Page} to iterate through all values (also in next pages): *

 {@code
  * Page page = ...; // get a Page instance
  * Iterator iterator = page.iterateAll();
@@ -30,8 +29,8 @@
  *   T value = iterator.next();
  *   // do something with value
  * }}
- *

- * Or handle pagination explicitly: + * + *

Or handle pagination explicitly: *

 {@code
  * Page page = ...; // get a Page instance
  * while (page != null) {
diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java b/gcloud-java-core/src/main/java/com/google/cloud/PageImpl.java
similarity index 99%
rename from gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java
rename to gcloud-java-core/src/main/java/com/google/cloud/PageImpl.java
index 2dc031ab9bd4..de1a3506b750 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java
+++ b/gcloud-java-core/src/main/java/com/google/cloud/PageImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.gcloud;
+package com.google.cloud;
 
 import com.google.common.collect.AbstractIterator;
 import com.google.common.collect.ImmutableMap;
diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ReadChannel.java b/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java
similarity index 98%
rename from gcloud-java-core/src/main/java/com/google/gcloud/ReadChannel.java
rename to gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java
index 7537c5a8ce0b..2afb8b2d5b32 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/ReadChannel.java
+++ b/gcloud-java-core/src/main/java/com/google/cloud/ReadChannel.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.gcloud;
+package com.google.cloud;
 
 import java.io.Closeable;
 import java.io.IOException;
diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Restorable.java b/gcloud-java-core/src/main/java/com/google/cloud/Restorable.java
similarity index 93%
rename from gcloud-java-core/src/main/java/com/google/gcloud/Restorable.java
rename to gcloud-java-core/src/main/java/com/google/cloud/Restorable.java
index 0b573522e370..e2a515c7861a 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/Restorable.java
+++ b/gcloud-java-core/src/main/java/com/google/cloud/Restorable.java
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
-package com.google.gcloud;
+package com.google.cloud;
 
 /**
  * Implementation of this interface can persist their state and restore from it.
  *
- * 

- * A typical capture usage: + *

A typical capture usage: *

 {@code
  * X restorableObj; // X instanceof Restorable
  * RestorableState state = restorableObj.capture();
  * .. persist state
  * }
* - * A typical restore usage: + *

A typical restore usage: *

 {@code
  * RestorableState state = ... // read from persistence
  * X restorableObj = state.restore();
diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/RestorableState.java b/gcloud-java-core/src/main/java/com/google/cloud/RestorableState.java
similarity index 89%
rename from gcloud-java-core/src/main/java/com/google/gcloud/RestorableState.java
rename to gcloud-java-core/src/main/java/com/google/cloud/RestorableState.java
index d6ce736ae856..4874756527f7 100644
--- a/gcloud-java-core/src/main/java/com/google/gcloud/RestorableState.java
+++ b/gcloud-java-core/src/main/java/com/google/cloud/RestorableState.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.google.gcloud;
+package com.google.cloud;
 
 /**
  * A common interface for restorable states. Implementations of {@code RestorableState} are capable
  * of saving the state of an object to restore it for later use.
  *
- * Implementations of this class must implement {@link java.io.Serializable} to ensure that the
+ * 

Implementations of this class must implement {@link java.io.Serializable} to ensure that the * state of a the object can be correctly serialized. * * @param the restored object's type diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java b/gcloud-java-core/src/main/java/com/google/cloud/RetryHelper.java similarity index 99% rename from gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java rename to gcloud-java-core/src/main/java/com/google/cloud/RetryHelper.java index 9b9c1f6a3124..48bd5fcf9324 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/RetryHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.StrictMath.max; diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java b/gcloud-java-core/src/main/java/com/google/cloud/RetryParams.java similarity index 91% rename from gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java rename to gcloud-java-core/src/main/java/com/google/cloud/RetryParams.java index ab3644c6d747..02ffda18134f 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/RetryParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.Preconditions.checkArgument; @@ -48,12 +48,16 @@ public final class RetryParams implements Serializable { private static final long serialVersionUID = -8492751576749007700L; + /** + * Note that App Engine Standard Environment front-end modules have a 60 second deadline for HTTP + * requests. For that reason, we set the default total retry period to less than 60 seconds. + */ + public static final long DEFAULT_TOTAL_RETRY_PERIOD_MILLIS = 50_000L; public static final int DEFAULT_RETRY_MIN_ATTEMPTS = 3; public static final int DEFAULT_RETRY_MAX_ATTEMPTS = 6; - public static final long DEFAULT_INITIAL_RETRY_DELAY_MILLIS = 250L; - public static final long DEFAULT_MAX_RETRY_DELAY_MILLIS = 10_000L; + public static final long DEFAULT_INITIAL_RETRY_DELAY_MILLIS = 1000L; + public static final long DEFAULT_MAX_RETRY_DELAY_MILLIS = 32_000L; public static final double DEFAULT_RETRY_DELAY_BACKOFF_FACTOR = 2.0; - public static final long DEFAULT_TOTAL_RETRY_PERIOD_MILLIS = 50_000L; private final int retryMinAttempts; private final int retryMaxAttempts; @@ -62,6 +66,9 @@ public final class RetryParams implements Serializable { private final double retryDelayBackoffFactor; private final long totalRetryPeriodMillis; + // Some services may have different backoff requirements listed in their SLAs. Be sure to override + // ServiceOptions.defaultRetryParams() in options subclasses when the service's backoff + // requirement differs from the default parameters used here. private static final RetryParams DEFAULT_INSTANCE = new RetryParams(new Builder()); private static final RetryParams NO_RETRIES = builder().retryMaxAttempts(1).retryMinAttempts(1).build(); @@ -156,7 +163,9 @@ public Builder retryDelayBackoffFactor(double retryDelayBackoffFactor) { } /** - * Sets totalRetryPeriodMillis. + * Sets totalRetryPeriodMillis. Note that App Engine Standard Environment front-end modules have + * a 60 second deadline for HTTP requests. For that reason, you should set the total retry + * period to under 60 seconds if you are using it on an App Engine front-end module. * * @param totalRetryPeriodMillis the totalRetryPeriodMillis to set * @return the Builder for chaining @@ -295,4 +304,8 @@ public String toString() { public static Builder builder() { return new Builder(); } + + public Builder toBuilder() { + return new Builder(this); + } } diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Service.java b/gcloud-java-core/src/main/java/com/google/cloud/Service.java similarity index 96% rename from gcloud-java-core/src/main/java/com/google/gcloud/Service.java rename to gcloud-java-core/src/main/java/com/google/cloud/Service.java index 60bc26670f2e..a3797eca30b9 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/Service.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/Service.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; /** * Interface for service objects. diff --git a/gcloud-java-core/src/main/java/com/google/cloud/ServiceAccountSigner.java b/gcloud-java-core/src/main/java/com/google/cloud/ServiceAccountSigner.java new file mode 100644 index 000000000000..c68d14116aa8 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/cloud/ServiceAccountSigner.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016 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.cloud; + +import java.util.Objects; + +/** + * Interface for a service account signer. A signer for a service account is capable of signing + * bytes using the private key associated with its service account. + */ +public interface ServiceAccountSigner { + + class SigningException extends RuntimeException { + + private static final long serialVersionUID = 8962780757822799255L; + + SigningException(String message, Exception cause) { + super(message, cause); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof SigningException)) { + return false; + } + SigningException other = (SigningException) obj; + return Objects.equals(getCause(), other.getCause()) + && Objects.equals(getMessage(), other.getMessage()); + } + + @Override + public int hashCode() { + return Objects.hash(getMessage(), getCause()); + } + } + + /** + * Returns the service account associated with the signer. + */ + String account(); + + /** + * Signs the provided bytes using the private key associated with the service account. + * + * @param toSign bytes to sign + * @return signed bytes + * @throws SigningException if the attempt to sign the provided bytes failed + */ + byte[] sign(byte[] toSign); +} diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceFactory.java b/gcloud-java-core/src/main/java/com/google/cloud/ServiceFactory.java similarity index 97% rename from gcloud-java-core/src/main/java/com/google/gcloud/ServiceFactory.java rename to gcloud-java-core/src/main/java/com/google/cloud/ServiceFactory.java index 1727e9c3976f..bb06127c77c6 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceFactory.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ServiceFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; /** * A base interface for all service factories. diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java b/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java similarity index 89% rename from gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java rename to gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java index d45069434a26..194f61b45c42 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; @@ -26,11 +26,17 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.auth.http.HttpCredentialsAdapter; +import com.google.cloud.spi.ServiceRpcFactory; import com.google.common.collect.Iterables; -import com.google.gcloud.spi.ServiceRpcFactory; +import com.google.common.io.Files; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; @@ -42,6 +48,7 @@ import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.Charset; import java.util.Enumeration; import java.util.Locale; import java.util.Objects; @@ -93,8 +100,8 @@ public abstract class ServiceOptions, Service /** * A base interface for all {@link HttpTransport} factories. * - * Implementation must provide a public no-arg constructor. Loading of a factory implementation is - * done via {@link java.util.ServiceLoader}. + *

Implementation must provide a public no-arg constructor. Loading of a factory implementation + * is done via {@link java.util.ServiceLoader}. */ public interface HttpTransportFactory { HttpTransport create(); @@ -122,7 +129,7 @@ public HttpTransport create() { * A class providing access to the current time in milliseconds. This class is mainly used for * testing and will be replaced by Java8's {@code java.time.Clock}. * - * Implementations should implement {@code Serializable} wherever possible and must document + *

Implementations should implement {@code Serializable} wherever possible and must document * whether or not they do support serialization. */ public abstract static class Clock { @@ -326,7 +333,7 @@ protected ServiceOptions(Class> ser authCredentials = builder.authCredentials != null ? builder.authCredentials : defaultAuthCredentials(); authCredentialsState = authCredentials != null ? authCredentials.capture() : null; - retryParams = firstNonNull(builder.retryParams, RetryParams.defaultInstance()); + retryParams = firstNonNull(builder.retryParams, defaultRetryParams()); serviceFactory = firstNonNull(builder.serviceFactory, getFromServiceLoader(serviceFactoryClass, defaultServiceFactory())); serviceFactoryClassName = serviceFactory.getClass().getName(); @@ -376,11 +383,26 @@ protected String defaultHost() { protected String defaultProject() { String projectId = System.getProperty(PROJECT_ENV_NAME, System.getenv(PROJECT_ENV_NAME)); if (projectId == null) { - projectId = getAppEngineProjectId(); + projectId = appEngineProjectId(); + } + if (projectId == null) { + projectId = serviceAccountProjectId(); } return projectId != null ? projectId : googleCloudProjectId(); } + private static String activeGoogleCloudConfig(File configDir) { + String activeGoogleCloudConfig = null; + try { + activeGoogleCloudConfig = + Files.readFirstLine(new File(configDir, "active_config"), Charset.defaultCharset()); + } catch (IOException ex) { + // ignore + } + // if reading active_config failed or the file is empty we try default + return firstNonNull(activeGoogleCloudConfig, "default"); + } + protected static String googleCloudProjectId() { File configDir; if (System.getenv().containsKey("CLOUDSDK_CONFIG")) { @@ -390,9 +412,10 @@ protected static String googleCloudProjectId() { } else { configDir = new File(System.getProperty("user.home"), ".config/gcloud"); } + String activeConfig = activeGoogleCloudConfig(configDir); FileReader fileReader = null; try { - fileReader = new FileReader(new File(configDir, "configurations/config_default")); + fileReader = new FileReader(new File(configDir, "configurations/config_" + activeConfig)); } catch (FileNotFoundException newConfigFileNotFoundEx) { try { fileReader = new FileReader(new File(configDir, "properties")); @@ -446,7 +469,7 @@ private static boolean isWindows() { return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"); } - protected static String getAppEngineProjectId() { + protected static String appEngineProjectId() { try { Class factoryClass = Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory"); @@ -464,6 +487,20 @@ protected static String getAppEngineProjectId() { } } + protected static String serviceAccountProjectId() { + String project = null; + String credentialsPath = System.getenv("GOOGLE_APPLICATION_CREDENTIALS"); + if (credentialsPath != null) { + try (InputStream credentialsStream = new FileInputStream(credentialsPath)) { + JSONObject json = new JSONObject(new JSONTokener(credentialsStream)); + project = json.getString("project_id"); + } catch (IOException | JSONException ex) { + // ignore + } + } + return project; + } + @SuppressWarnings("unchecked") public ServiceT service() { if (service == null) { @@ -481,9 +518,8 @@ public ServiceRpcT rpc() { } /** - * Returns the project id. - * - * Return value can be null (for services that don't require a project id). + * Returns the project id. Return value can be null (for services that don't require a project + * id). */ public String projectId() { return projectId; @@ -610,14 +646,23 @@ private static T newInstance(String className) throws IOException, ClassNotF } } - protected abstract > T defaultServiceFactory(); + protected abstract ServiceFactory defaultServiceFactory(); - protected abstract > T defaultRpcFactory(); + protected abstract ServiceRpcFactory defaultRpcFactory(); protected abstract Set scopes(); public abstract > B toBuilder(); + /** + * Some services may have different backoff requirements listed in their SLAs. Be sure to override + * this method in options subclasses when the service's backoff requirement differs from the + * default parameters listed in {@link RetryParams}. + */ + protected RetryParams defaultRetryParams() { + return RetryParams.defaultInstance(); + } + private static T getFromServiceLoader(Class clazz, T defaultInstance) { return Iterables.getFirst(ServiceLoader.load(clazz), defaultInstance); } diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/WriteChannel.java b/gcloud-java-core/src/main/java/com/google/cloud/WriteChannel.java similarity index 98% rename from gcloud-java-core/src/main/java/com/google/gcloud/WriteChannel.java rename to gcloud-java-core/src/main/java/com/google/cloud/WriteChannel.java index e6f06e23dc04..636bc60d0243 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/WriteChannel.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/WriteChannel.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import java.io.Closeable; import java.nio.channels.WritableByteChannel; diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/package-info.java b/gcloud-java-core/src/main/java/com/google/cloud/package-info.java similarity index 96% rename from gcloud-java-core/src/main/java/com/google/gcloud/package-info.java rename to gcloud-java-core/src/main/java/com/google/cloud/package-info.java index d527640c99f9..8b548e3d26e8 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/package-info.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/package-info.java @@ -17,4 +17,4 @@ /** * Core classes for the {@code gcloud-java} library. */ -package com.google.gcloud; +package com.google.cloud; diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/spi/ServiceRpcFactory.java b/gcloud-java-core/src/main/java/com/google/cloud/spi/ServiceRpcFactory.java similarity index 74% rename from gcloud-java-core/src/main/java/com/google/gcloud/spi/ServiceRpcFactory.java rename to gcloud-java-core/src/main/java/com/google/cloud/spi/ServiceRpcFactory.java index d19f6047e4b2..21a061f4a5dc 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/spi/ServiceRpcFactory.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/spi/ServiceRpcFactory.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.spi; +package com.google.cloud.spi; -import com.google.gcloud.ServiceOptions; +import com.google.cloud.ServiceOptions; /** - * A base interface for all service RPC factories. - * - * Implementation must provide a public no-arg constructor. - * Loading of a factory implementation is done via {@link java.util.ServiceLoader}. + * A base interface for all service RPC factories. Implementation must provide a public no-arg + * constructor. Loading of a factory implementation is done via {@link java.util.ServiceLoader}. */ @SuppressWarnings("rawtypes") public interface ServiceRpcFactory { diff --git a/gcloud-java-core/src/test/java/com/google/cloud/BaseSerializationTest.java b/gcloud-java-core/src/test/java/com/google/cloud/BaseSerializationTest.java new file mode 100644 index 000000000000..57603b9026d8 --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/cloud/BaseSerializationTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 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.cloud; + +import static com.google.common.base.MoreObjects.firstNonNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * Base class for serialization tests. To use this class in your tests override the + * {@code serializableObjects()} method to return all objects that must be serializable. Also + * override {@code restorableObjects()} method to return all restorable objects whose state must be + * tested for proper serialization. Both methods can return {@code null} if no such object needs to + * be tested. + */ +public abstract class BaseSerializationTest { + + /** + * Returns all objects for which correct serialization must be tested. + */ + protected abstract Serializable[] serializableObjects(); + + /** + * Returns all restorable objects whose state must be tested for proper serialization. + */ + protected abstract Restorable[] restorableObjects(); + + @Test + public void testSerializableObjects() throws Exception { + for (Serializable obj : firstNonNull(serializableObjects(), new Serializable[0])) { + Object copy = serializeAndDeserialize(obj); + assertEquals(obj, obj); + assertEquals(obj, copy); + assertEquals(obj.hashCode(), copy.hashCode()); + assertEquals(obj.toString(), copy.toString()); + assertNotSame(obj, copy); + assertEquals(copy, copy); + } + } + + @Test + public void testRestorableObjects() throws Exception { + for (Restorable restorable : firstNonNull(restorableObjects(), new Restorable[0])) { + RestorableState state = restorable.capture(); + RestorableState deserializedState = serializeAndDeserialize(state); + assertEquals(state, deserializedState); + assertEquals(state.hashCode(), deserializedState.hashCode()); + assertEquals(state.toString(), deserializedState.toString()); + } + } + + @SuppressWarnings("unchecked") + public T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { + output.writeObject(obj); + } + try (ObjectInputStream input = + new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { + return (T) input.readObject(); + } + } +} diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java b/gcloud-java-core/src/test/java/com/google/cloud/BaseServiceExceptionTest.java similarity index 98% rename from gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/BaseServiceExceptionTest.java index e3c6abb7d1ee..52bc3b2b51a4 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/BaseServiceExceptionTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; -import static com.google.gcloud.BaseServiceException.UNKNOWN_CODE; +import static com.google.cloud.BaseServiceException.UNKNOWN_CODE; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/BaseWriteChannelTest.java b/gcloud-java-core/src/test/java/com/google/cloud/BaseWriteChannelTest.java similarity index 96% rename from gcloud-java-core/src/test/java/com/google/gcloud/BaseWriteChannelTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/BaseWriteChannelTest.java index 6d5306a3bc7f..fc476b11ae6e 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/BaseWriteChannelTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/BaseWriteChannelTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; @@ -22,7 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import com.google.gcloud.spi.ServiceRpcFactory; +import com.google.cloud.spi.ServiceRpcFactory; import org.junit.Before; import org.junit.Rule; @@ -82,7 +82,7 @@ protected BaseState.Builder stateBuilder() { } @Test - public void testConstructor() throws IOException { + public void testConstructor() { assertEquals(null, channel.options()); assertEquals(ENTITY, channel.entity()); assertEquals(0, channel.position()); @@ -108,7 +108,7 @@ public void testValidateOpen() throws IOException { } @Test - public void testChunkSize() throws IOException { + public void testChunkSize() { channel.chunkSize(42); assertEquals(MIN_CHUNK_SIZE, channel.chunkSize()); channel.chunkSize(2 * MIN_CHUNK_SIZE); diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/gcloud-java-core/src/test/java/com/google/cloud/ExceptionHandlerTest.java similarity index 97% rename from gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/ExceptionHandlerTest.java index cedc995ddbd0..ddbd2ced841c 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/ExceptionHandlerTest.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.gcloud.ExceptionHandler.Interceptor; -import com.google.gcloud.ExceptionHandler.Interceptor.RetryResult; +import com.google.cloud.ExceptionHandler.Interceptor; +import com.google.cloud.ExceptionHandler.Interceptor.RetryResult; import org.junit.Rule; import org.junit.Test; 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 new file mode 100644 index 000000000000..02d5847946e0 --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/cloud/FieldSelectorHelperTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 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.cloud; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.FieldSelector.Helper; +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.util.List; + +public class FieldSelectorHelperTest { + + private static final FieldSelector FIELD1 = new FieldSelector() { + @Override + public String selector() { + return "field1"; + } + }; + private static final FieldSelector FIELD2 = new FieldSelector() { + @Override + public String selector() { + return "field2"; + } + }; + private static final FieldSelector FIELD3 = new FieldSelector() { + @Override + 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"; + + @Test + public void testSelector() { + String selector = Helper.selector(REQUIRED_FIELDS, FIELD3); + assertTrue(selector.contains("field1")); + assertTrue(selector.contains("field2")); + assertTrue(selector.contains("field3")); + assertEquals(20, selector.length()); + } + + @Test + public void testListSelector() { + String selector = Helper.listSelector(CONTAINER, REQUIRED_FIELDS, FIELD3); + assertTrue(selector.startsWith("nextPageToken,container(")); + assertTrue(selector.contains("field1")); + assertTrue(selector.contains("field2")); + assertTrue(selector.contains("field3")); + assertTrue(selector.endsWith(")")); + assertEquals(45, selector.length()); + } + + @Test + public void testListSelectorWithExtraFields() { + String selector = Helper.listSelector(CONTAINER, REQUIRED_FIELDS, + new FieldSelector[]{FIELD3}, "field4"); + assertTrue(selector.startsWith("nextPageToken,container(")); + assertTrue(selector.contains("field1")); + assertTrue(selector.contains("field2")); + assertTrue(selector.contains("field3")); + assertTrue(selector.contains("field4")); + 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-core/src/test/java/com/google/gcloud/IamPolicyTest.java b/gcloud-java-core/src/test/java/com/google/cloud/IamPolicyTest.java similarity index 70% rename from gcloud-java-core/src/test/java/com/google/gcloud/IamPolicyTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/IamPolicyTest.java index db0935c4766d..e79cfd96554c 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/IamPolicyTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/IamPolicyTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -28,6 +28,8 @@ import org.junit.Test; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -46,8 +48,8 @@ public class IamPolicyTest { "editor", ImmutableSet.of(ALL_AUTH_USERS, GROUP, DOMAIN)); private static final PolicyImpl SIMPLE_POLICY = PolicyImpl.builder() - .addBinding("viewer", ImmutableSet.of(USER, SERVICE_ACCOUNT, ALL_USERS)) - .addBinding("editor", ImmutableSet.of(ALL_AUTH_USERS, GROUP, DOMAIN)) + .addIdentity("viewer", USER, SERVICE_ACCOUNT, ALL_USERS) + .addIdentity("editor", ALL_AUTH_USERS, GROUP, DOMAIN) .build(); private static final PolicyImpl FULL_POLICY = new PolicyImpl.Builder(SIMPLE_POLICY.bindings(), "etag", 1).build(); @@ -93,7 +95,7 @@ public void testBuilder() { assertEquals(editorBinding, policy.bindings()); assertEquals("etag", policy.etag()); assertEquals(1, policy.version().intValue()); - policy = SIMPLE_POLICY.toBuilder().removeBinding("editor").build(); + policy = SIMPLE_POLICY.toBuilder().removeRole("editor").build(); assertEquals(ImmutableMap.of("viewer", BINDINGS.get("viewer")), policy.bindings()); assertNull(policy.etag()); assertNull(policy.version()); @@ -105,22 +107,61 @@ public void testBuilder() { policy.bindings()); assertNull(policy.etag()); assertNull(policy.version()); - policy = PolicyImpl.builder().addBinding("owner", USER, SERVICE_ACCOUNT).build(); + policy = PolicyImpl.builder() + .removeIdentity("viewer", USER) + .addIdentity("owner", USER, SERVICE_ACCOUNT) + .addIdentity("editor", GROUP) + .removeIdentity("editor", GROUP) + .build(); assertEquals( ImmutableMap.of("owner", ImmutableSet.of(USER, SERVICE_ACCOUNT)), policy.bindings()); assertNull(policy.etag()); assertNull(policy.version()); + } + + @Test + public void testIllegalPolicies() { + try { + PolicyImpl.builder().addIdentity(null, USER); + fail("Null role should cause exception."); + } catch (NullPointerException ex) { + assertEquals("The role cannot be null.", ex.getMessage()); + } + try { + PolicyImpl.builder().addIdentity("viewer", null, USER); + fail("Null identity should cause exception."); + } catch (NullPointerException ex) { + assertEquals("Null identities are not permitted.", ex.getMessage()); + } + try { + PolicyImpl.builder().addIdentity("viewer", USER, (Identity[]) null); + fail("Null identity should cause exception."); + } catch (NullPointerException ex) { + assertEquals("Null identities are not permitted.", ex.getMessage()); + } + try { + PolicyImpl.builder().bindings(null); + fail("Null bindings map should cause exception."); + } catch (NullPointerException ex) { + assertEquals("The provided map of bindings cannot be null.", ex.getMessage()); + } try { - SIMPLE_POLICY.toBuilder().addBinding("viewer", USER); - fail("Should have failed due to duplicate role."); - } catch (IllegalArgumentException e) { - assertEquals("The policy already contains a binding with the role viewer.", e.getMessage()); + Map> bindings = new HashMap<>(); + bindings.put("viewer", null); + PolicyImpl.builder().bindings(bindings); + fail("Null set of identities should cause exception."); + } catch (NullPointerException ex) { + assertEquals("A role cannot be assigned to a null set of identities.", ex.getMessage()); } try { - SIMPLE_POLICY.toBuilder().addBinding("editor", ImmutableSet.of(USER)); - fail("Should have failed due to duplicate role."); - } catch (IllegalArgumentException e) { - assertEquals("The policy already contains a binding with the role editor.", e.getMessage()); + Map> bindings = new HashMap<>(); + Set identities = new HashSet<>(); + identities.add(null); + bindings.put("viewer", identities); + PolicyImpl.builder().bindings(bindings); + fail("Null identity should cause exception."); + } catch (IllegalArgumentException ex) { + assertEquals("Null identities are not permitted.", ex.getMessage()); } } diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/IdentityTest.java b/gcloud-java-core/src/test/java/com/google/cloud/IdentityTest.java similarity index 88% rename from gcloud-java-core/src/test/java/com/google/gcloud/IdentityTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/IdentityTest.java index 828f1c839431..e720503c547d 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/IdentityTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/IdentityTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -34,19 +34,19 @@ public class IdentityTest { @Test public void testAllUsers() { assertEquals(Identity.Type.ALL_USERS, ALL_USERS.type()); - assertNull(ALL_USERS.id()); + assertNull(ALL_USERS.value()); } @Test public void testAllAuthenticatedUsers() { assertEquals(Identity.Type.ALL_AUTHENTICATED_USERS, ALL_AUTH_USERS.type()); - assertNull(ALL_AUTH_USERS.id()); + assertNull(ALL_AUTH_USERS.value()); } @Test public void testUser() { assertEquals(Identity.Type.USER, USER.type()); - assertEquals("abc@gmail.com", USER.id()); + assertEquals("abc@gmail.com", USER.value()); } @Test(expected = NullPointerException.class) @@ -57,7 +57,7 @@ public void testUserNullEmail() { @Test public void testServiceAccount() { assertEquals(Identity.Type.SERVICE_ACCOUNT, SERVICE_ACCOUNT.type()); - assertEquals("service-account@gmail.com", SERVICE_ACCOUNT.id()); + assertEquals("service-account@gmail.com", SERVICE_ACCOUNT.value()); } @Test(expected = NullPointerException.class) @@ -68,7 +68,7 @@ public void testServiceAccountNullEmail() { @Test public void testGroup() { assertEquals(Identity.Type.GROUP, GROUP.type()); - assertEquals("group@gmail.com", GROUP.id()); + assertEquals("group@gmail.com", GROUP.value()); } @Test(expected = NullPointerException.class) @@ -79,7 +79,7 @@ public void testGroupNullEmail() { @Test public void testDomain() { assertEquals(Identity.Type.DOMAIN, DOMAIN.type()); - assertEquals("google.com", DOMAIN.id()); + assertEquals("google.com", DOMAIN.value()); } @Test(expected = NullPointerException.class) @@ -100,6 +100,6 @@ public void testIdentityToAndFromPb() { private void compareIdentities(Identity expected, Identity actual) { assertEquals(expected, actual); assertEquals(expected.type(), actual.type()); - assertEquals(expected.id(), actual.id()); + assertEquals(expected.value(), actual.value()); } } diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java b/gcloud-java-core/src/test/java/com/google/cloud/PageImplTest.java similarity index 98% rename from gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/PageImplTest.java index 4389171fb49c..07d979ad857c 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/PageImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java b/gcloud-java-core/src/test/java/com/google/cloud/RetryHelperTest.java similarity index 98% rename from gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/RetryHelperTest.java index 9a7cc2104f4a..3887cecd6a83 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/RetryHelperTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static java.util.concurrent.Executors.callable; import static org.junit.Assert.assertEquals; @@ -22,10 +22,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.RetryHelper.NonRetriableException; +import com.google.cloud.RetryHelper.RetriesExhaustedException; import com.google.common.base.Stopwatch; import com.google.common.base.Ticker; -import com.google.gcloud.RetryHelper.NonRetriableException; -import com.google.gcloud.RetryHelper.RetriesExhaustedException; import org.junit.Test; diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java b/gcloud-java-core/src/test/java/com/google/cloud/RetryParamsTest.java similarity index 87% rename from gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/RetryParamsTest.java index eae44693929b..c3041053110b 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/RetryParamsTest.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; -import static com.google.gcloud.RetryParams.DEFAULT_INITIAL_RETRY_DELAY_MILLIS; -import static com.google.gcloud.RetryParams.DEFAULT_MAX_RETRY_DELAY_MILLIS; -import static com.google.gcloud.RetryParams.DEFAULT_RETRY_DELAY_BACKOFF_FACTOR; -import static com.google.gcloud.RetryParams.DEFAULT_RETRY_MAX_ATTEMPTS; -import static com.google.gcloud.RetryParams.DEFAULT_RETRY_MIN_ATTEMPTS; -import static com.google.gcloud.RetryParams.DEFAULT_TOTAL_RETRY_PERIOD_MILLIS; +import static com.google.cloud.RetryParams.DEFAULT_INITIAL_RETRY_DELAY_MILLIS; +import static com.google.cloud.RetryParams.DEFAULT_MAX_RETRY_DELAY_MILLIS; +import static com.google.cloud.RetryParams.DEFAULT_RETRY_DELAY_BACKOFF_FACTOR; +import static com.google.cloud.RetryParams.DEFAULT_RETRY_MAX_ATTEMPTS; +import static com.google.cloud.RetryParams.DEFAULT_RETRY_MIN_ATTEMPTS; +import static com.google.cloud.RetryParams.DEFAULT_TOTAL_RETRY_PERIOD_MILLIS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import com.google.gcloud.RetryParams.Builder; +import com.google.cloud.RetryParams.Builder; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java b/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java new file mode 100644 index 000000000000..75347b250227 --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2016 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.cloud; + +import com.google.cloud.ServiceAccountSigner.SigningException; +import com.google.common.collect.ImmutableList; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Serializable; + +public class SerializationTest extends BaseSerializationTest { + + private static final BaseServiceException BASE_SERVICE_EXCEPTION = + new BaseServiceException(42, "message", "reason", true); + private static final ExceptionHandler EXCEPTION_HANDLER = ExceptionHandler.defaultInstance(); + private static final Identity IDENTITY = Identity.allAuthenticatedUsers(); + private static final PageImpl PAGE = + new PageImpl<>(null, "cursor", ImmutableList.of("string1", "string2")); + private static final SigningException SIGNING_EXCEPTION = + new SigningException("message", BASE_SERVICE_EXCEPTION); + private static final RetryParams RETRY_PARAMS = RetryParams.defaultInstance(); + private static final SomeIamPolicy SOME_IAM_POLICY = new SomeIamPolicy.Builder().build(); + private static final String JSON_KEY = "{\n" + + " \"private_key_id\": \"somekeyid\",\n" + + " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggS" + + "kAgEAAoIBAQC+K2hSuFpAdrJI\\nnCgcDz2M7t7bjdlsadsasad+fvRSW6TjNQZ3p5LLQY1kSZRqBqylRkzteMOyHg" + + "aR\\n0Pmxh3ILCND5men43j3h4eDbrhQBuxfEMalkG92sL+PNQSETY2tnvXryOvmBRwa/\\nQP/9dJfIkIDJ9Fw9N4" + + "Bhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nknddadwkwewcVxHFhcZJO+XWf6ofLUXpRwiTZakGMn8EE1uVa2" + + "LgczOjwWHGi99MFjxSer5m9\\n1tCa3/KEGKiS/YL71JvjwX3mb+cewlkcmweBKZHM2JPTk0ZednFSpVZMtycjkbLa" + + "\\ndYOS8V85AgMBewECggEBAKksaldajfDZDV6nGqbFjMiizAKJolr/M3OQw16K6o3/\\n0S31xIe3sSlgW0+UbYlF" + + "4U8KifhManD1apVSC3csafaspP4RZUHFhtBywLO9pR5c\\nr6S5aLp+gPWFyIp1pfXbWGvc5VY/v9x7ya1VEa6rXvL" + + "sKupSeWAW4tMj3eo/64ge\\nsdaceaLYw52KeBYiT6+vpsnYrEkAHO1fF/LavbLLOFJmFTMxmsNaG0tuiJHgjshB\\" + + "n82DpMCbXG9YcCgI/DbzuIjsdj2JC1cascSP//3PmefWysucBQe7Jryb6NQtASmnv\\nCdDw/0jmZTEjpe4S1lxfHp" + + "lAhHFtdgYTvyYtaLZiVVkCgYEA8eVpof2rceecw/I6\\n5ng1q3Hl2usdWV/4mZMvR0fOemacLLfocX6IYxT1zA1FF" + + "JlbXSRsJMf/Qq39mOR2\\nSpW+hr4jCoHeRVYLgsbggtrevGmILAlNoqCMpGZ6vDmJpq6ECV9olliDvpPgWOP+\\nm" + + "YPDreFBGxWvQrADNbRt2dmGsrsCgYEAyUHqB2wvJHFqdmeBsaacewzV8x9WgmeX\\ngUIi9REwXlGDW0Mz50dxpxcK" + + "CAYn65+7TCnY5O/jmL0VRxU1J2mSWyWTo1C+17L0\\n3fUqjxL1pkefwecxwecvC+gFFYdJ4CQ/MHHXU81Lwl1iWdF" + + "Cd2UoGddYaOF+KNeM\\nHC7cmqra+JsCgYEAlUNywzq8nUg7282E+uICfCB0LfwejuymR93CtsFgb7cRd6ak\\nECR" + + "8FGfCpH8ruWJINllbQfcHVCX47ndLZwqv3oVFKh6pAS/vVI4dpOepP8++7y1u\\ncoOvtreXCX6XqfrWDtKIvv0vjl" + + "HBhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nkndj5uNl5SiuVxHFhcZJO+XWf6ofLUregtevZakGMn8EE1uVa" + + "2AY7eafmoU/nZPT\\n00YB0TBATdCbn/nBSuKDESkhSg9s2GEKQZG5hBmL5uCMfo09z3SfxZIhJdlerreP\\nJ7gSi" + + "dI12N+EZxYd4xIJh/HFDgp7RRO87f+WJkofMQKBgGTnClK1VMaCRbJZPriw\\nEfeFCoOX75MxKwXs6xgrw4W//AYG" + + "GUjDt83lD6AZP6tws7gJ2IwY/qP7+lyhjEqN\\nHtfPZRGFkGZsdaksdlaksd323423d+15/UvrlRSFPNj1tWQmNKk" + + "XyRDW4IG1Oa2p\\nrALStNBx5Y9t0/LQnFI4w3aG\\n-----END PRIVATE KEY-----\\n\",\n" + + " \"client_email\": \"someclientid@developer.gserviceaccount.com\",\n" + + " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n" + + " \"type\": \"service_account\"\n" + + "}"; + + private static class SomeIamPolicy extends IamPolicy { + + private static final long serialVersionUID = 271243551016958285L; + + private static class Builder extends IamPolicy.Builder { + + @Override + public SomeIamPolicy build() { + return new SomeIamPolicy(this); + } + } + + protected SomeIamPolicy(Builder builder) { + super(builder); + } + + @Override + public Builder toBuilder() { + return new Builder(); + } + } + + @Override + protected Serializable[] serializableObjects() { + return new Serializable[]{BASE_SERVICE_EXCEPTION, EXCEPTION_HANDLER, IDENTITY, PAGE, + RETRY_PARAMS, SOME_IAM_POLICY, SIGNING_EXCEPTION}; + } + + @Override + protected Restorable[] restorableObjects() { + try { + return new Restorable[]{AuthCredentials.createForAppEngine(), AuthCredentials.noAuth(), + AuthCredentials.createForJson(new ByteArrayInputStream(JSON_KEY.getBytes()))}; + } catch (IOException ex) { + // never reached + throw new RuntimeException(ex); + } + } +} diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/ServiceOptionsTest.java b/gcloud-java-core/src/test/java/com/google/cloud/ServiceOptionsTest.java similarity index 97% rename from gcloud-java-core/src/test/java/com/google/gcloud/ServiceOptionsTest.java rename to gcloud-java-core/src/test/java/com/google/cloud/ServiceOptionsTest.java index d0e3db2d2a55..b6f7a5453ddc 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/ServiceOptionsTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/ServiceOptionsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud; +package com.google.cloud; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -22,10 +22,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.gcloud.ServiceOptions.Clock; -import com.google.gcloud.ServiceOptions.DefaultHttpTransportFactory; -import com.google.gcloud.ServiceOptions.HttpTransportFactory; -import com.google.gcloud.spi.ServiceRpcFactory; +import com.google.cloud.ServiceOptions.Clock; +import com.google.cloud.ServiceOptions.DefaultHttpTransportFactory; +import com.google.cloud.ServiceOptions.HttpTransportFactory; +import com.google.cloud.spi.ServiceRpcFactory; import org.easymock.EasyMock; import org.junit.Test; diff --git a/gcloud-java-datastore/README.md b/gcloud-java-datastore/README.md index 0d89a0a07e3e..86b77c6bf49a 100644 --- a/gcloud-java-datastore/README.md +++ b/gcloud-java-datastore/README.md @@ -5,12 +5,12 @@ Java idiomatic client for [Google Cloud Datastore] (https://cloud.google.com/dat [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-datastore.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-datastore.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-datastore.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-datastore.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/datastore/package-summary.html) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/datastore/package-summary.html) > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. @@ -20,23 +20,23 @@ Quickstart If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-datastore - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-datastore:0.1.5' +compile 'com.google.cloud:gcloud-java-datastore:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-datastore" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-datastore" % "0.2.0" ``` Example Application -------------------- -[`DatastoreExample`](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/DatastoreExample.java) is a simple command line interface for the Cloud Datastore. Read more about using the application on the [`DatastoreExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/datastore/DatastoreExample.html). +[`DatastoreExample`](../gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java) is a simple command line interface for the Cloud Datastore. Read more about using the application on the [`DatastoreExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/datastore/DatastoreExample.html). Authentication -------------- @@ -70,8 +70,8 @@ You'll need to obtain the `gcloud-java-datastore` library. See the [Quickstart] To make authenticated requests to Google Cloud Datastore, you must create a service object with credentials. You can then make API calls by calling methods on the Datastore 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.datastore.Datastore; -import com.google.gcloud.datastore.DatastoreOptions; +import com.google.cloud.datastore.Datastore; +import com.google.cloud.datastore.DatastoreOptions; Datastore datastore = DatastoreOptions.defaultInstance().service(); ``` @@ -82,9 +82,9 @@ For other authentication options, see the [Authentication](https://github.com/Go Objects in Datastore are known as entities. Entities are grouped by "kind" and have keys for easy access. In this code snippet, we will create a new entity representing a person and store that data by the person's email. First, add the following imports at the top of your file: ```java -import com.google.gcloud.datastore.Entity; -import com.google.gcloud.datastore.Key; -import com.google.gcloud.datastore.KeyFactory; +import com.google.cloud.datastore.Entity; +import com.google.cloud.datastore.Key; +import com.google.cloud.datastore.KeyFactory; ``` Then add the following code to put an entity in Datastore. @@ -112,10 +112,10 @@ In addition to retrieving entities by their keys, you can perform queries to ret Suppose that you've added more people to Datastore, and now you want to find all people whose favorite food is pizza. Import the following: ```java -import com.google.gcloud.datastore.Query; -import com.google.gcloud.datastore.QueryResults; -import com.google.gcloud.datastore.StructuredQuery; -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; +import com.google.cloud.datastore.Query; +import com.google.cloud.datastore.QueryResults; +import com.google.cloud.datastore.StructuredQuery; +import com.google.cloud.datastore.StructuredQuery.PropertyFilter; ``` Then add the following code to your program: @@ -137,7 +137,7 @@ Cloud Datastore relies on indexing to run queries. Indexing is turned on by defa #### Complete source code In -[AddEntitiesAndRunQuery.java](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java) +[AddEntitiesAndRunQuery.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java) we put together all the code shown above into one program. The program assumes that you are running on Compute Engine or from your own desktop. To run the example on App Engine, simply move the code from the main method to your application's servlet class and change the print statements to @@ -191,4 +191,4 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-platform]: https://cloud.google.com/ [cloud-datastore-docs]: https://cloud.google.com/datastore/docs [cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate -[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/datastore/package-summary.html +[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/datastore/package-summary.html diff --git a/gcloud-java-datastore/pom.xml b/gcloud-java-datastore/pom.xml index 977b6db22b14..2e76072b2881 100644 --- a/gcloud-java-datastore/pom.xml +++ b/gcloud-java-datastore/pom.xml @@ -4,13 +4,14 @@ gcloud-java-datastore jar GCloud Java datastore + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-datastore Java idiomatic client for Google Cloud Datastore. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-datastore @@ -22,16 +23,21 @@ ${project.version} - com.google.apis - google-api-services-datastore-protobuf - v1beta2-rev1-4.0.0 - compile - - - com.google.api-client - google-api-client - - + com.google.cloud.datastore + datastore-v1beta3-protos + 1.0.0-beta + + + com.google.cloud.datastore + datastore-v1beta3-proto-client + 1.0.0-beta + + + ${project.groupId} + gcloud-java-core + ${project.version} + test-jar + test junit diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseDatastoreBatchWriter.java similarity index 87% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseDatastoreBatchWriter.java index b42c07d62320..8804ef7e8dda 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseDatastoreBatchWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseDatastoreBatchWriter.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; @@ -88,9 +88,7 @@ public final List add(FullEntity... entities) { for (FullEntity entity : entities) { IncompleteKey key = entity.key(); Preconditions.checkArgument(key != null, "Entity must have a key"); - if (key instanceof Key) { - addInternal((FullEntity) entity); - } else { + if (!(key instanceof Key)) { incompleteKeys.add(key); } } @@ -104,6 +102,7 @@ public final List add(FullEntity... entities) { List answer = Lists.newArrayListWithExpectedSize(entities.length); for (FullEntity entity : entities) { if (entity.key() instanceof Key) { + addInternal((FullEntity) entity); answer.add(Entity.convert((FullEntity) entity)); } else { Entity entityWithAllocatedId = Entity.builder(allocated.next(), entity).build(); @@ -199,24 +198,30 @@ protected DatastoreException newInvalidRequest(String msg, Object... params) { return DatastoreException.throwInvalidRequest(String.format(msg, params)); } - DatastoreV1.Mutation.Builder toMutationPb() { - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); + protected List toMutationPbList() { + List mutationsPb = + new ArrayList<>(); for (FullEntity entity : toAddAutoId()) { - mutationPb.addInsertAutoId(entity.toPb()); + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setInsert(entity.toPb()).build()); } for (FullEntity entity : toAdd().values()) { - mutationPb.addInsert(entity.toPb()); + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setInsert(entity.toPb()).build()); } for (FullEntity entity : toUpdate().values()) { - mutationPb.addUpdate(entity.toPb()); + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(entity.toPb()).build()); } for (FullEntity entity : toPut().values()) { - mutationPb.addUpsert(entity.toPb()); + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); } for (Key key : toDelete()) { - mutationPb.addDelete(key.toPb()); + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(key.toPb()).build()); } - return mutationPb; + return mutationsPb; } protected abstract Datastore datastore(); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseEntity.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseEntity.java index 20c0b13e5001..c7417faa79f3 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseEntity.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseEntity.java @@ -14,20 +14,20 @@ * limitations under the License. */ -package com.google.gcloud.datastore; - -import static com.google.gcloud.datastore.BlobValue.of; -import static com.google.gcloud.datastore.BooleanValue.of; -import static com.google.gcloud.datastore.DateTimeValue.of; -import static com.google.gcloud.datastore.DoubleValue.of; -import static com.google.gcloud.datastore.EntityValue.of; -import static com.google.gcloud.datastore.KeyValue.of; -import static com.google.gcloud.datastore.ListValue.of; -import static com.google.gcloud.datastore.LongValue.of; -import static com.google.gcloud.datastore.NullValue.of; -import static com.google.gcloud.datastore.StringValue.of; - -import com.google.api.services.datastore.DatastoreV1; +package com.google.cloud.datastore; + +import static com.google.cloud.datastore.BlobValue.of; +import static com.google.cloud.datastore.BooleanValue.of; +import static com.google.cloud.datastore.DateTimeValue.of; +import static com.google.cloud.datastore.DoubleValue.of; +import static com.google.cloud.datastore.EntityValue.of; +import static com.google.cloud.datastore.KeyValue.of; +import static com.google.cloud.datastore.LatLngValue.of; +import static com.google.cloud.datastore.ListValue.of; +import static com.google.cloud.datastore.LongValue.of; +import static com.google.cloud.datastore.NullValue.of; +import static com.google.cloud.datastore.StringValue.of; + import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Maps; import com.google.protobuf.InvalidProtocolBufferException; @@ -43,13 +43,14 @@ * A base class for entities (key and properties). * An entity is a Google Cloud Datastore persistent data object. * An entity holds one or more properties, represented by a name (as {@link String}) - * and a value (as {@link com.google.gcloud.datastore.Value}), and may be associated with a + * and a value (as {@link com.google.cloud.datastore.Value}), and may be associated with a * key. For a list of possible values see {@link ValueType}. * * @see Google Cloud Datastore * Entities, Properties, and Keys */ -public abstract class BaseEntity extends Serializable { +public abstract class BaseEntity + extends Serializable { private static final long serialVersionUID = 8175618724683792766L; @@ -91,10 +92,11 @@ private B self() { } @SuppressWarnings("unchecked") - B fill(DatastoreV1.Entity entityPb) { + B fill(com.google.datastore.v1beta3.Entity entityPb) { Map> copiedProperties = Maps.newHashMap(); - for (DatastoreV1.Property property : entityPb.getPropertyList()) { - copiedProperties.put(property.getName(), Value.fromPb(property.getValue())); + for (Map.Entry entry : + entityPb.getProperties().entrySet()) { + copiedProperties.put(entry.getKey(), Value.fromPb(entry.getValue())); } properties(copiedProperties); if (entityPb.hasKey()) { @@ -290,6 +292,36 @@ public B set(String name, DateTime first, DateTime second, DateTime... others) { return self(); } + /** + * Sets a property of type {@link LatLng}. + * + * @param name name of the property + * @param value value associated with the property + */ + public B set(String name, LatLng value) { + properties.put(name, of(value)); + return self(); + } + + /** + * Sets a list property containing elements of type {@link LatLng}. + * + * @param name name of the property + * @param first the first {@link LatLng} in the list + * @param second the second {@link LatLng} in the list + * @param others other {@link LatLng}s in the list + */ + public B set(String name, LatLng first, LatLng second, LatLng... others) { + List values = new LinkedList<>(); + values.add(of(first)); + values.add(of(second)); + for (LatLng other : others) { + values.add(of(other)); + } + properties.put(name, of(values)); + return self(); + } + /** * Sets a property of type {@link KeyValue}. * @@ -545,6 +577,17 @@ public DateTime getDateTime(String name) { return ((Value) getValue(name)).get(); } + /** + * Returns the property value as a LatLng. + * + * @throws DatastoreException if not such property. + * @throws ClassCastException if value is not a LatLng. + */ + @SuppressWarnings("unchecked") + public LatLng getLatLng(String name) { + return ((Value) getValue(name)).get(); + } + /** * Returns the property value as a Key. * @@ -603,20 +646,19 @@ ImmutableSortedMap> properties() { @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { Builder builder = emptyBuilder(); - builder.fill(DatastoreV1.Entity.parseFrom(bytesPb)); + builder.fill(com.google.datastore.v1beta3.Entity.parseFrom(bytesPb)); return builder.build(); } protected abstract Builder emptyBuilder(); @Override - final DatastoreV1.Entity toPb() { - DatastoreV1.Entity.Builder entityPb = DatastoreV1.Entity.newBuilder(); + final com.google.datastore.v1beta3.Entity toPb() { + com.google.datastore.v1beta3.Entity.Builder entityPb = + com.google.datastore.v1beta3.Entity.newBuilder(); + Map propertiesPb = entityPb.getMutableProperties(); for (Map.Entry> entry : properties.entrySet()) { - DatastoreV1.Property.Builder propertyPb = DatastoreV1.Property.newBuilder(); - propertyPb.setName(entry.getKey()); - propertyPb.setValue(entry.getValue().toPb()); - entityPb.addProperty(propertyPb.build()); + propertiesPb.put(entry.getKey(), entry.getValue().toPb()); } if (key != null) { entityPb.setKey(key.toPb()); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseKey.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseKey.java similarity index 82% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseKey.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseKey.java index 4ab6f51b6767..73f98562e0e5 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BaseKey.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BaseKey.java @@ -14,13 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.gcloud.datastore.Validator.validateDatabase; -import static com.google.gcloud.datastore.Validator.validateKind; -import static com.google.gcloud.datastore.Validator.validateNamespace; +import static com.google.cloud.datastore.Validator.validateDatabase; +import static com.google.cloud.datastore.Validator.validateKind; +import static com.google.cloud.datastore.Validator.validateNamespace; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -31,7 +30,7 @@ /** * Base class for keys. */ -public abstract class BaseKey extends Serializable { +public abstract class BaseKey extends Serializable { private static final long serialVersionUID = -4671243265877410635L; @@ -46,8 +45,8 @@ public abstract class BaseKey extends Serializable { */ protected abstract static class Builder> { - String projectId; - String namespace; + String projectId = ""; + String namespace = ""; String kind; final List ancestors; @@ -179,20 +178,15 @@ public boolean equals(Object obj) { } @Override - DatastoreV1.Key toPb() { - DatastoreV1.Key.Builder keyPb = DatastoreV1.Key.newBuilder(); - DatastoreV1.PartitionId.Builder partitionIdPb = DatastoreV1.PartitionId.newBuilder(); - if (projectId != null) { - partitionIdPb.setDatasetId(projectId); - } - if (namespace != null) { - partitionIdPb.setNamespace(namespace); - } - if (partitionIdPb.hasDatasetId() || partitionIdPb.hasNamespace()) { - keyPb.setPartitionId(partitionIdPb.build()); - } + com.google.datastore.v1beta3.Key toPb() { + com.google.datastore.v1beta3.Key.Builder keyPb = com.google.datastore.v1beta3.Key.newBuilder(); + com.google.datastore.v1beta3.PartitionId.Builder partitionIdPb = + com.google.datastore.v1beta3.PartitionId.newBuilder(); + partitionIdPb.setProjectId(projectId); + partitionIdPb.setNamespaceId(namespace); + keyPb.setPartitionId(partitionIdPb.build()); for (PathElement pathEntry : path) { - keyPb.addPathElement(pathEntry.toPb()); + keyPb.addPath(pathEntry.toPb()); } return keyPb.build(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Batch.java similarity index 97% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Batch.java index 5306a685195a..dbe6f86c4b99 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Batch.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Batch.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.List; diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BatchImpl.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BatchImpl.java new file mode 100644 index 000000000000..603e9464c9c1 --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BatchImpl.java @@ -0,0 +1,72 @@ +/* + * 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.cloud.datastore; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +class BatchImpl extends BaseDatastoreBatchWriter implements Batch { + + private final DatastoreImpl datastore; + + static class ResponseImpl implements Batch.Response { + + private final com.google.datastore.v1beta3.CommitResponse response; + private final int numAutoAllocatedIds; + + ResponseImpl(com.google.datastore.v1beta3.CommitResponse response, int numAutoAllocatedIds) { + this.response = response; + this.numAutoAllocatedIds = numAutoAllocatedIds; + } + + @Override + public List generatedKeys() { + Iterator results = + response.getMutationResultsList().iterator(); + List generated = new ArrayList<>(numAutoAllocatedIds); + for (int i = 0; i < numAutoAllocatedIds; i++) { + generated.add(Key.fromPb(results.next().getKey())); + } + return generated; + } + } + + BatchImpl(DatastoreImpl datastore) { + super("batch"); + this.datastore = datastore; + } + + @Override + public Batch.Response submit() { + validateActive(); + List mutationsPb = toMutationPbList(); + com.google.datastore.v1beta3.CommitRequest.Builder requestPb = + com.google.datastore.v1beta3.CommitRequest.newBuilder(); + requestPb.setMode(com.google.datastore.v1beta3.CommitRequest.Mode.NON_TRANSACTIONAL); + requestPb.addAllMutations(mutationsPb); + com.google.datastore.v1beta3.CommitResponse responsePb = datastore.commit(requestPb.build()); + deactivate(); + return new ResponseImpl(responsePb, toAddAutoId().size()); + } + + @Override + public Datastore datastore() { + return datastore; + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Blob.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Blob.java similarity index 91% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Blob.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Blob.java index 42a98e60b5e6..0fd7213d7be3 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Blob.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Blob.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Value; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.protobuf.ByteString; @@ -38,7 +36,7 @@ * @see * Google Cloud Datastore Entities, Properties, and Keys */ -public final class Blob extends Serializable { +public final class Blob extends Serializable { private static final long serialVersionUID = 3835421019618247721L; @@ -147,12 +145,12 @@ public static Blob copyFrom(InputStream input) throws IOException { } @Override - Value toPb() { - return DatastoreV1.Value.newBuilder().setBlobValue(byteString).build(); + com.google.datastore.v1beta3.Value toPb() { + return com.google.datastore.v1beta3.Value.newBuilder().setBlobValue(byteString).build(); } @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return new Blob(DatastoreV1.Value.parseFrom(bytesPb).getBlobValue()); + return new Blob(com.google.datastore.v1beta3.Value.parseFrom(bytesPb).getBlobValue()); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BlobValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BlobValue.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BlobValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BlobValue.java index fb61c0b9ad34..d65bb6ee8652 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BlobValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BlobValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.BLOB_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.BLOB_VALUE_FIELD_NUMBER; public final class BlobValue extends Value { @@ -40,12 +38,12 @@ public Builder newBuilder(Blob value) { } @Override - protected Blob getValue(DatastoreV1.Value from) { + protected Blob getValue(com.google.datastore.v1beta3.Value from) { return new Blob(from.getBlobValue()); } @Override - protected void setValue(BlobValue from, DatastoreV1.Value.Builder to) { + protected void setValue(BlobValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setBlobValue(from.get().byteString()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BooleanValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BooleanValue.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BooleanValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BooleanValue.java index 2dd98a5013ac..c5d7910b5984 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BooleanValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/BooleanValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.BOOLEAN_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.BOOLEAN_VALUE_FIELD_NUMBER; public final class BooleanValue extends Value { @@ -40,12 +38,12 @@ public Builder newBuilder(Boolean value) { } @Override - protected Boolean getValue(DatastoreV1.Value from) { + protected Boolean getValue(com.google.datastore.v1beta3.Value from) { return from.getBooleanValue(); } @Override - protected void setValue(BooleanValue from, DatastoreV1.Value.Builder to) { + protected void setValue(BooleanValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setBooleanValue(from.get()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Cursor.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Cursor.java similarity index 86% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Cursor.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Cursor.java index 5e577f7feb6c..f9d51638b35b 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Cursor.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Cursor.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Value; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.io.BaseEncoding; @@ -30,7 +28,7 @@ * A Google Cloud Datastore cursor. * The cursor can be used to as a starting point or an ending point for a {@link Query} */ -public final class Cursor extends Serializable { +public final class Cursor extends Serializable { private static final long serialVersionUID = -1423744878777486541L; @@ -87,16 +85,16 @@ public static Cursor copyFrom(byte[] bytes) { } @Override - Value toPb() { - return DatastoreV1.Value.newBuilder().setBlobValue(byteString).build(); + com.google.datastore.v1beta3.Value toPb() { + return com.google.datastore.v1beta3.Value.newBuilder().setBlobValue(byteString).build(); } @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Value.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.Value.parseFrom(bytesPb)); } - static Cursor fromPb(DatastoreV1.Value valuePb) { + static Cursor fromPb(com.google.datastore.v1beta3.Value valuePb) { return new Cursor(valuePb.getBlobValue()); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Datastore.java similarity index 63% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Datastore.java index 870ed8d9474f..8b2afcb5ad0c 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Datastore.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Datastore.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.gcloud.Service; +import com.google.cloud.Service; +import java.util.Iterator; import java.util.List; /** @@ -30,12 +31,11 @@ public interface Datastore extends Service, DatastoreReaderWri * * @throws DatastoreException upon failure */ - Transaction newTransaction(TransactionOption... options); - + Transaction newTransaction(); /** * A callback for running with a transactional - * {@link com.google.gcloud.datastore.DatastoreReaderWriter}. + * {@link com.google.cloud.datastore.DatastoreReaderWriter}. * The associated transaction will be committed after a successful return from the {@code run} * method. Any propagated exception will cause the transaction to be rolled-back. * @@ -45,7 +45,6 @@ interface TransactionCallable { T run(DatastoreReaderWriter readerWriter) throws Exception; } - /** * Invokes the callback's {@link Datastore.TransactionCallable#run} method with a * {@link DatastoreReaderWriter} that is associated with a new transaction. @@ -54,15 +53,14 @@ interface TransactionCallable { * as a {@link DatastoreException} with the original exception as its root cause. * * @param callable the callback to call with a newly created transactional readerWriter - * @param options the options for the created transaction * @throws DatastoreException upon failure */ - T runInTransaction(TransactionCallable callable, TransactionOption... options); + T runInTransaction(TransactionCallable callable); /** * Returns a new Batch for processing multiple write operations in one request. */ - Batch newBatch(BatchOption... options); + Batch newBatch(); /** * Allocate a unique id for the given key. @@ -106,4 +104,39 @@ interface TransactionCallable { * Returns a new KeyFactory for this service */ KeyFactory newKeyFactory(); + + /** + * Returns an {@link Entity} for the given {@link Key} or {@code null} if it doesn't exist. + * {@link ReadOption}s can be specified if desired. + * + * @throws DatastoreException upon failure + */ + Entity get(Key key, ReadOption... options); + + /** + * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore. The order of + * the result is unspecified. Results are loaded lazily, so it is possible to get a + * {@code DatastoreException} from the returned {@code Iterator}'s + * {@link Iterator#hasNext hasNext} or {@link Iterator#next next} methods. {@link ReadOption}s can + * be specified if desired. + * + * @throws DatastoreException upon failure + * @see #get(Key) + */ + Iterator get(Iterable keys, ReadOption... options); + + /** + * Returns a list with a value for each given key (ordered by input). {@code null} values are + * returned for nonexistent keys. When possible prefer using {@link #get(Key...)} to avoid eagerly + * loading the results. {@link ReadOption}s can be specified if desired. + */ + List fetch(Iterable keys, ReadOption... options); + + /** + * Submits a {@link Query} and returns its result. {@link ReadOption}s can be specified if + * desired. + * + * @throws DatastoreException upon failure + */ + QueryResults run(Query query, ReadOption... options); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreBatchWriter.java similarity index 98% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreBatchWriter.java index 3a80452349dc..918711aa2822 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreBatchWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreBatchWriter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.List; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreException.java similarity index 89% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreException.java index ecad69ac635b..419cffc7bf3e 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreException.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryHelper.RetryInterruptedException; import java.io.IOException; import java.util.Set; @@ -34,9 +34,7 @@ public class DatastoreException extends BaseServiceException { // see https://cloud.google.com/datastore/docs/concepts/errors#Error_Codes" private static final Set RETRYABLE_ERRORS = ImmutableSet.of( - new Error(409, "ABORTED"), - new Error(403, "DEADLINE_EXCEEDED"), - new Error(503, "UNAVAILABLE")); + new Error(10, "ABORTED"), new Error(4, "DEADLINE_EXCEEDED"), new Error(14, "UNAVAILABLE")); private static final long serialVersionUID = 2663750991205874435L; public DatastoreException(int code, String message, String reason, Throwable cause) { diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreFactory.java similarity index 90% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreFactory.java index b1f5a026a3e5..1a1b49cf71b3 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreFactory.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreFactory.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.gcloud.ServiceFactory; +import com.google.cloud.ServiceFactory; /** * An interface for Datastore factories. diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreHelper.java similarity index 64% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreHelper.java index a74d06642740..04b3e96c5175 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreHelper.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreHelper.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -33,13 +35,16 @@ class DatastoreHelper { private DatastoreHelper() { } - static Key allocateId(Datastore service, IncompleteKey key) { return service.allocateId(new IncompleteKey[]{key}).get(0); } - static Entity get(DatastoreReader reader, Key key) { - return Iterators.getNext(reader.get(new Key[]{key}), null); + static Entity get(Transaction reader, Key key) { + return Iterators.getNext(reader.get(new Key[] {key}), null); + } + + static Entity get(Datastore reader, Key key, ReadOption... options) { + return Iterators.getNext(reader.get(Collections.singletonList(key), options), null); } static Entity add(DatastoreWriter writer, FullEntity entity) { @@ -51,11 +56,22 @@ static KeyFactory newKeyFactory(DatastoreOptions options) { } /** - * Returns a list with a value for each given key (ordered by input). - * A {@code null} would be returned for non-existing keys. + * Returns a list with a value for each given key (ordered by input). {@code null} values are + * returned for nonexistent keys. */ - static List fetch(DatastoreReader reader, Key... keys) { - Iterator entities = reader.get(keys); + static List fetch(Transaction reader, Key... keys) { + return compileEntities(keys, reader.get(keys)); + } + + /** + * Returns a list with a value for each given key (ordered by input). {@code null} values are + * returned for nonexistent keys. + */ + static List fetch(Datastore reader, Key[] keys, ReadOption... options) { + return compileEntities(keys, reader.get(Arrays.asList(keys), options)); + } + + private static List compileEntities(Key[] keys, Iterator entities) { Map map = Maps.newHashMapWithExpectedSize(keys.length); while (entities.hasNext()) { Entity entity = entities.next(); @@ -63,15 +79,14 @@ static List fetch(DatastoreReader reader, Key... keys) { } List list = new ArrayList<>(keys.length); for (Key key : keys) { - // this will include nulls for non-existing keys + // this will include nulls for nonexistent keys list.add(map.get(key)); } return list; } - static T runInTransaction(Datastore datastore, - Datastore.TransactionCallable callable, TransactionOption... options) { - Transaction transaction = datastore.newTransaction(options); + static T runInTransaction(Datastore datastore, Datastore.TransactionCallable callable) { + Transaction transaction = datastore.newTransaction(); try { T value = callable.run(transaction); transaction.commit(); diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java new file mode 100644 index 000000000000..2aa0638bfa7c --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java @@ -0,0 +1,416 @@ +/* + * 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.cloud.datastore; + +import com.google.cloud.BaseService; +import com.google.cloud.RetryHelper; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryParams; +import com.google.cloud.datastore.ReadOption.EventualConsistency; +import com.google.cloud.datastore.spi.DatastoreRpc; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.datastore.v1beta3.ReadOptions.ReadConsistency; +import com.google.protobuf.ByteString; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +final class DatastoreImpl extends BaseService implements Datastore { + + private final DatastoreRpc datastoreRpc; + private final RetryParams retryParams; + + DatastoreImpl(DatastoreOptions options) { + super(options); + this.datastoreRpc = options.rpc(); + retryParams = MoreObjects.firstNonNull(options.retryParams(), RetryParams.noRetries()); + } + + @Override + public Batch newBatch() { + return new BatchImpl(this); + } + + @Override + public Transaction newTransaction() { + return new TransactionImpl(this); + } + + @Override + public T runInTransaction(TransactionCallable callable) { + return DatastoreHelper.runInTransaction(this, callable); + } + + @Override + public QueryResults run(Query query) { + return run(null, query); + } + + @Override + public QueryResults run(Query query, ReadOption... options) { + return run(toReadOptionsPb(options), query); + } + + QueryResults run(com.google.datastore.v1beta3.ReadOptions readOptionsPb, Query query) { + return new QueryResultsImpl<>(this, readOptionsPb, query); + } + + com.google.datastore.v1beta3.RunQueryResponse runQuery( + final com.google.datastore.v1beta3.RunQueryRequest requestPb) { + try { + return RetryHelper.runWithRetries( + new Callable() { + @Override public com.google.datastore.v1beta3.RunQueryResponse call() + throws DatastoreException { + return datastoreRpc.runQuery(requestPb); + } + }, retryParams, EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } + + @Override + public Key allocateId(IncompleteKey key) { + return DatastoreHelper.allocateId(this, key); + } + + @Override + public List allocateId(IncompleteKey... keys) { + if (keys.length == 0) { + return Collections.emptyList(); + } + com.google.datastore.v1beta3.AllocateIdsRequest.Builder requestPb = + com.google.datastore.v1beta3.AllocateIdsRequest.newBuilder(); + for (IncompleteKey key : keys) { + requestPb.addKeys(trimNameOrId(key).toPb()); + } + com.google.datastore.v1beta3.AllocateIdsResponse responsePb = allocateIds(requestPb.build()); + ImmutableList.Builder keyList = ImmutableList.builder(); + for (com.google.datastore.v1beta3.Key keyPb : responsePb.getKeysList()) { + keyList.add(Key.fromPb(keyPb)); + } + return keyList.build(); + } + + com.google.datastore.v1beta3.AllocateIdsResponse allocateIds( + final com.google.datastore.v1beta3.AllocateIdsRequest requestPb) { + try { + return RetryHelper.runWithRetries( + new Callable() { + @Override public com.google.datastore.v1beta3.AllocateIdsResponse call() + throws DatastoreException { + return datastoreRpc.allocateIds(requestPb); + } + }, retryParams, EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } + + private IncompleteKey trimNameOrId(IncompleteKey key) { + if (key instanceof Key) { + return IncompleteKey.builder(key).build(); + } + return key; + } + + @Override + public Entity add(FullEntity entity) { + return DatastoreHelper.add(this, entity); + } + + @SuppressWarnings("unchecked") + @Override + public List add(FullEntity... entities) { + if (entities.length == 0) { + return Collections.emptyList(); + } + List mutationsPb = new ArrayList<>(); + Map completeEntities = new LinkedHashMap<>(); + for (FullEntity entity : entities) { + Entity completeEntity = null; + if (entity.key() instanceof Key) { + completeEntity = Entity.convert((FullEntity) entity); + } + if (completeEntity != null) { + if (completeEntities.put(completeEntity.key(), completeEntity) != null) { + throw DatastoreException.throwInvalidRequest( + "Duplicate entity with the key %s", entity.key()); + } + } else { + Preconditions.checkArgument(entity.hasKey(), "entity %s is missing a key", entity); + } + mutationsPb.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(entity.toPb()).build()); + } + com.google.datastore.v1beta3.CommitResponse commitResponse = commitMutation(mutationsPb); + Iterator mutationResults = + commitResponse.getMutationResultsList().iterator(); + ImmutableList.Builder responseBuilder = ImmutableList.builder(); + for (FullEntity entity : entities) { + Entity completeEntity = completeEntities.get(entity.key()); + if (completeEntity != null) { + responseBuilder.add(completeEntity); + mutationResults.next(); + } else { + responseBuilder.add( + Entity.builder(Key.fromPb(mutationResults.next().getKey()), entity).build()); + } + } + return responseBuilder.build(); + } + + @Override + public Entity get(Key key) { + return DatastoreHelper.get(this, key); + } + + @Override + public Entity get(Key key, ReadOption... options) { + return DatastoreHelper.get(this, key, options); + } + + @Override + public Iterator get(Key... keys) { + return get(null, keys); + } + + @Override + public Iterator get(Iterable keys, ReadOption... options) { + return get(toReadOptionsPb(options), Iterables.toArray(keys, Key.class)); + } + + private static com.google.datastore.v1beta3.ReadOptions toReadOptionsPb(ReadOption... options) { + com.google.datastore.v1beta3.ReadOptions readOptionsPb = null; + if (options != null + && ReadOption.asImmutableMap(options).containsKey(EventualConsistency.class)) { + readOptionsPb = com.google.datastore.v1beta3.ReadOptions.newBuilder() + .setReadConsistency(ReadConsistency.EVENTUAL) + .build(); + } + return readOptionsPb; + } + + @Override + public List fetch(Key... keys) { + return DatastoreHelper.fetch(this, keys); + } + + @Override + public List fetch(Iterable keys, ReadOption... options) { + return DatastoreHelper.fetch(this, Iterables.toArray(keys, Key.class), options); + } + + Iterator get(com.google.datastore.v1beta3.ReadOptions readOptionsPb, final Key... keys) { + if (keys.length == 0) { + return Collections.emptyIterator(); + } + com.google.datastore.v1beta3.LookupRequest.Builder requestPb = + com.google.datastore.v1beta3.LookupRequest.newBuilder(); + if (readOptionsPb != null) { + requestPb.setReadOptions(readOptionsPb); + } + for (Key k : Sets.newLinkedHashSet(Arrays.asList(keys))) { + requestPb.addKeys(k.toPb()); + } + return new ResultsIterator(requestPb); + } + + final class ResultsIterator extends AbstractIterator { + + private final com.google.datastore.v1beta3.LookupRequest.Builder requestPb; + Iterator iter; + + ResultsIterator(com.google.datastore.v1beta3.LookupRequest.Builder requestPb) { + this.requestPb = requestPb; + loadResults(); + } + + private void loadResults() { + com.google.datastore.v1beta3.LookupResponse responsePb = lookup(requestPb.build()); + iter = responsePb.getFoundList().iterator(); + requestPb.clearKeys(); + if (responsePb.getDeferredCount() > 0) { + requestPb.addAllKeys(responsePb.getDeferredList()); + } + } + + @SuppressWarnings("unchecked") + @Override + protected Entity computeNext() { + while (!iter.hasNext()) { + if (requestPb.getKeysCount() == 0) { + return endOfData(); + } + loadResults(); + } + return Entity.fromPb(iter.next().getEntity()); + } + } + + com.google.datastore.v1beta3.LookupResponse lookup( + final com.google.datastore.v1beta3.LookupRequest requestPb) { + try { + return RetryHelper.runWithRetries( + new Callable() { + @Override public com.google.datastore.v1beta3.LookupResponse call() + throws DatastoreException { + return datastoreRpc.lookup(requestPb); + } + }, retryParams, EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } + + @SafeVarargs + @Override + public final void update(Entity... entities) { + if (entities.length > 0) { + List mutationsPb = + new ArrayList<>(); + Map dedupEntities = new LinkedHashMap<>(); + for (Entity entity : entities) { + dedupEntities.put(entity.key(), entity); + } + for (Entity entity : dedupEntities.values()) { + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(entity.toPb()).build()); + } + commitMutation(mutationsPb); + } + } + + @SafeVarargs + @Override + public final void put(Entity... entities) { + if (entities.length > 0) { + List mutationsPb = + new ArrayList<>(); + Map dedupEntities = new LinkedHashMap<>(); + for (Entity entity : entities) { + dedupEntities.put(entity.key(), entity); + } + for (Entity e : dedupEntities.values()) { + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(e.toPb()).build()); + } + commitMutation(mutationsPb); + } + } + + @Override + public void delete(Key... keys) { + if (keys.length > 0) { + List mutationsPb = new ArrayList<>(); + Set dedupKeys = new LinkedHashSet<>(Arrays.asList(keys)); + for (Key key : dedupKeys) { + mutationsPb.add( + com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(key.toPb()).build()); + } + commitMutation(mutationsPb); + } + } + + @Override + public KeyFactory newKeyFactory() { + return DatastoreHelper.newKeyFactory(options()); + } + + private com.google.datastore.v1beta3.CommitResponse commitMutation( + List mutationsPb) { + com.google.datastore.v1beta3.CommitRequest.Builder requestPb = + com.google.datastore.v1beta3.CommitRequest.newBuilder(); + requestPb.setMode(com.google.datastore.v1beta3.CommitRequest.Mode.NON_TRANSACTIONAL); + requestPb.addAllMutations(mutationsPb); + return commit(requestPb.build()); + } + + com.google.datastore.v1beta3.CommitResponse commit( + final com.google.datastore.v1beta3.CommitRequest requestPb) { + try { + return RetryHelper.runWithRetries( + new Callable() { + @Override + public com.google.datastore.v1beta3.CommitResponse call() throws DatastoreException { + return datastoreRpc.commit(requestPb); + } + }, + retryParams, + EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } + + ByteString requestTransactionId( + com.google.datastore.v1beta3.BeginTransactionRequest.Builder requestPb) { + return beginTransaction(requestPb.build()).getTransaction(); + } + + com.google.datastore.v1beta3.BeginTransactionResponse beginTransaction( + final com.google.datastore.v1beta3.BeginTransactionRequest requestPb) { + try { + return RetryHelper.runWithRetries( + new Callable() { + @Override + public com.google.datastore.v1beta3.BeginTransactionResponse call() + throws DatastoreException { + return datastoreRpc.beginTransaction(requestPb); + } + }, + retryParams, + EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } + + void rollbackTransaction(ByteString transaction) { + com.google.datastore.v1beta3.RollbackRequest.Builder requestPb = + com.google.datastore.v1beta3.RollbackRequest.newBuilder(); + requestPb.setTransaction(transaction); + rollback(requestPb.build()); + } + + void rollback(final com.google.datastore.v1beta3.RollbackRequest requestPb) { + try { + RetryHelper.runWithRetries(new Callable() { + @Override public Void call() throws DatastoreException { + datastoreRpc.rollback(requestPb); + return null; + } + }, retryParams, EXCEPTION_HANDLER); + } catch (RetryHelperException e) { + throw DatastoreException.translateAndThrow(e); + } + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreOptions.java similarity index 53% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreOptions.java index db1a5f800ce8..d456af51bde1 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreOptions.java @@ -14,37 +14,28 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.gcloud.datastore.Validator.validateNamespace; +import static com.google.cloud.datastore.Validator.validateNamespace; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.EntityResult; -import com.google.api.services.datastore.DatastoreV1.LookupResponse; +import com.google.cloud.ServiceOptions; +import com.google.cloud.datastore.spi.DatastoreRpc; +import com.google.cloud.datastore.spi.DatastoreRpcFactory; +import com.google.cloud.datastore.spi.DefaultDatastoreRpc; +import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.datastore.spi.DatastoreRpc; -import com.google.gcloud.datastore.spi.DatastoreRpcFactory; -import com.google.gcloud.datastore.spi.DefaultDatastoreRpc; import java.lang.reflect.Method; -import java.util.Iterator; import java.util.Objects; import java.util.Set; public class DatastoreOptions extends ServiceOptions { private static final long serialVersionUID = 5056049000758143852L; - private static final String DATASET_ENV_NAME = "DATASTORE_DATASET"; - private static final String HOST_ENV_NAME = "DATASTORE_HOST"; private static final String DATASTORE_SCOPE = "https://www.googleapis.com/auth/datastore"; - private static final String USERINFO_SCOPE = "https://www.googleapis.com/auth/userinfo.email"; - private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE, USERINFO_SCOPE); + private static final Set SCOPES = ImmutableSet.of(DATASTORE_SCOPE); private final String namespace; - private final boolean force; - private final boolean normalizeDataset; public static class DefaultDatastoreFactory implements DatastoreFactory { @@ -70,95 +61,52 @@ public static class Builder extends ServiceOptions.Builder { private String namespace; - private boolean force; - private boolean normalizeDataset = true; private Builder() { } private Builder(DatastoreOptions options) { super(options); - force = options.force; namespace = options.namespace; - normalizeDataset = options.normalizeDataset; } @Override public DatastoreOptions build() { - DatastoreOptions options = new DatastoreOptions(this); - return normalizeDataset ? options.normalize() : options; + return new DatastoreOptions(this); } public Builder namespace(String namespace) { this.namespace = validateNamespace(namespace); return this; } - - public Builder force(boolean force) { - this.force = force; - return this; - } - - Builder normalizeDataset(boolean normalizeDataset) { - this.normalizeDataset = normalizeDataset; - return this; - } } private DatastoreOptions(Builder builder) { super(DatastoreFactory.class, DatastoreRpcFactory.class, builder); - normalizeDataset = builder.normalizeDataset; namespace = builder.namespace != null ? builder.namespace : defaultNamespace(); - force = builder.force; - } - - private DatastoreOptions normalize() { - if (!normalizeDataset) { - return this; - } - - Builder builder = toBuilder(); - builder.normalizeDataset(false); - // Replace provided project-id with full project-id (s~xxx, e~xxx,...) - DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder(); - DatastoreV1.Key key = DatastoreV1.Key.newBuilder() - .addPathElement(DatastoreV1.Key.PathElement.newBuilder().setKind("__foo__").setName("bar")) - .build(); - requestPb.addKey(key); - LookupResponse responsePb = rpc().lookup(requestPb.build()); - if (responsePb.getDeferredCount() > 0) { - key = responsePb.getDeferred(0); - } else { - Iterator combinedIter = - Iterables.concat(responsePb.getMissingList(), responsePb.getFoundList()).iterator(); - key = combinedIter.next().getEntity().getKey(); - } - builder.projectId(key.getPartitionId().getDatasetId()); - return new DatastoreOptions(builder); } @Override protected String defaultHost() { - String host = System.getProperty(HOST_ENV_NAME, System.getenv(HOST_ENV_NAME)); - return host != null ? host : super.defaultHost(); + String host = System.getProperty( + com.google.datastore.v1beta3.client.DatastoreHelper.LOCAL_HOST_ENV_VAR, + System.getenv(com.google.datastore.v1beta3.client.DatastoreHelper.LOCAL_HOST_ENV_VAR)); + return host != null ? host : com.google.datastore.v1beta3.client.DatastoreFactory.DEFAULT_HOST; } @Override protected String defaultProject() { - String projectId = System.getProperty(DATASET_ENV_NAME, System.getenv(DATASET_ENV_NAME)); - if (projectId == null) { - projectId = appEngineAppId(); - } + String projectId = System.getProperty( + com.google.datastore.v1beta3.client.DatastoreHelper.PROJECT_ID_ENV_VAR, + System.getenv(com.google.datastore.v1beta3.client.DatastoreHelper.PROJECT_ID_ENV_VAR)); return projectId != null ? projectId : super.defaultProject(); } - @SuppressWarnings("unchecked") @Override protected DatastoreFactory defaultServiceFactory() { return DefaultDatastoreFactory.INSTANCE; } - @SuppressWarnings("unchecked") @Override protected DatastoreRpcFactory defaultRpcFactory() { return DefaultDatastoreRpcFactory.INSTANCE; @@ -180,17 +128,13 @@ private static String defaultNamespace() { Class clazz = Class.forName("com.google.appengine.api.NamespaceManager"); Method method = clazz.getMethod("get"); String namespace = (String) method.invoke(null); - return namespace == null || namespace.isEmpty() ? null : namespace; + return MoreObjects.firstNonNull(namespace, ""); } catch (Exception ignore) { - // return null (Datastore default namespace) if could not automatically determine - return null; + // return empty string (Datastore default namespace) if could not automatically determine + return ""; } } - public boolean force() { - return force; - } - @Override protected Set scopes() { return SCOPES; @@ -204,7 +148,7 @@ public Builder toBuilder() { @Override public int hashCode() { - return baseHashCode() ^ Objects.hash(namespace, force, normalizeDataset); + return Objects.hash(baseHashCode(), namespace); } @Override @@ -213,9 +157,7 @@ public boolean equals(Object obj) { return false; } DatastoreOptions other = (DatastoreOptions) obj; - return baseEquals(other) && Objects.equals(namespace, other.namespace) - && Objects.equals(force, other.force) - && Objects.equals(normalizeDataset, other.normalizeDataset); + return baseEquals(other) && Objects.equals(namespace, other.namespace); } public static Builder builder() { diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReader.java similarity index 70% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReader.java index 4852dd53e16c..e3989f29ef8b 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReader.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.Iterator; import java.util.List; @@ -25,18 +25,17 @@ public interface DatastoreReader { /** - * Returns an {@link Entity} for the given {@link Key} or {@code null} if does not exists. + * Returns an {@link Entity} for the given {@link Key} or {@code null} if it doesn't exist. * * @throws DatastoreException upon failure */ Entity get(Key key); /** - * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore. - * The order of the result is unspecified. - * Results are loaded lazily therefore it is possible to get a {@code DatastoreException} - * from the returned {@code Iterator}'s {@link Iterator#hasNext hasNext} or - * {@link Iterator#next next} methods. + * Returns an {@link Entity} for each given {@link Key} that exists in the Datastore. The order of + * the result is unspecified. Results are loaded lazily, so it is possible to get a + * {@code DatastoreException} from the returned {@code Iterator}'s + * {@link Iterator#hasNext hasNext} or {@link Iterator#next next} methods. * * @throws DatastoreException upon failure * @see #get(Key) @@ -44,14 +43,14 @@ public interface DatastoreReader { Iterator get(Key... key); /** - * Returns a list with a value for each given key (ordered by input). - * A {@code null} would be returned for non-existing keys. - * When possible prefer using {@link #get(Key...)} which does not eagerly loads the results. + * Returns a list with a value for each given key (ordered by input). {@code null} values are + * returned for nonexistent keys. When possible prefer using {@link #get(Key...)} to avoid eagerly + * loading the results. */ List fetch(Key... keys); /** - * Submit a {@link Query} and returns its result. + * Submits a {@link Query} and returns its result. * * @throws DatastoreException upon failure */ diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReaderWriter.java similarity index 95% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReaderWriter.java index c64f86a8d0a3..16b6af881929 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreReaderWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreReaderWriter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; /** diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreWriter.java similarity index 98% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreWriter.java index 66ba98aed9e9..2771bb39ea60 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreWriter.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DatastoreWriter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.List; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTime.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTime.java similarity index 76% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTime.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTime.java index d22edd9697e4..fd72edc9806f 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTime.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTime.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.Value; import com.google.protobuf.InvalidProtocolBufferException; import org.joda.time.format.ISODateTimeFormat; @@ -34,7 +32,7 @@ * @see Google Cloud Datastore * Entities, Properties, and Keys */ -public final class DateTime extends Serializable +public final class DateTime extends Serializable implements Comparable { private static final long serialVersionUID = 7343324797621228378L; @@ -98,12 +96,23 @@ public static DateTime copyFrom(Calendar calendar) { } @Override - Value toPb() { - return DatastoreV1.Value.newBuilder().setIntegerValue(timestampMicroseconds).build(); + com.google.protobuf.Timestamp toPb() { + return microsecondsToTimestampPb(timestampMicroseconds); } @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return new DateTime(DatastoreV1.Value.parseFrom(bytesPb).getIntegerValue()); + return new DateTime(timestampPbToMicroseconds( + com.google.protobuf.Timestamp.parseFrom(bytesPb))); + } + + static long timestampPbToMicroseconds(com.google.protobuf.Timestamp timestampPb) { + return timestampPb.getSeconds() * 1000000 + timestampPb.getNanos() / 1000; + } + + static com.google.protobuf.Timestamp microsecondsToTimestampPb(long microseconds) { + long seconds = microseconds / 1000000; + int nanos = (int) (microseconds % 1000000) * 1000; + return com.google.protobuf.Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTimeValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTimeValue.java similarity index 77% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTimeValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTimeValue.java index 7aec5c7d3c47..8ffa98ea5e74 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DateTimeValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DateTimeValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.TIMESTAMP_VALUE_FIELD_NUMBER; public final class DateTimeValue extends Value { @@ -31,7 +29,7 @@ public final class DateTimeValue extends Value { @Override public int getProtoFieldId() { - return TIMESTAMP_MICROSECONDS_VALUE_FIELD_NUMBER; + return TIMESTAMP_VALUE_FIELD_NUMBER; } @Override @@ -40,13 +38,14 @@ public Builder newBuilder(DateTime value) { } @Override - protected DateTime getValue(DatastoreV1.Value from) { - return new DateTime(from.getTimestampMicrosecondsValue()); + protected DateTime getValue(com.google.datastore.v1beta3.Value from) { + return new DateTime(DateTime.timestampPbToMicroseconds(from.getTimestampValue())); } @Override - protected void setValue(DateTimeValue from, DatastoreV1.Value.Builder to) { - to.setTimestampMicrosecondsValue(from.get().timestampMicroseconds()); + protected void setValue(DateTimeValue from, com.google.datastore.v1beta3.Value.Builder to) { + to.setTimestampValue(DateTime.microsecondsToTimestampPb(from.get() + .timestampMicroseconds())); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DoubleValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DoubleValue.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DoubleValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DoubleValue.java index d12bbe317aef..51d175bb51c4 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DoubleValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/DoubleValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.DOUBLE_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.DOUBLE_VALUE_FIELD_NUMBER; public final class DoubleValue extends Value { @@ -40,12 +38,12 @@ public Builder newBuilder(Double value) { } @Override - protected Double getValue(DatastoreV1.Value from) { + protected Double getValue(com.google.datastore.v1beta3.Value from) { return from.getDoubleValue(); } @Override - protected void setValue(DoubleValue from, DatastoreV1.Value.Builder to) { + protected void setValue(DoubleValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setDoubleValue(from.get()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Entity.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Entity.java similarity index 94% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Entity.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Entity.java index dc1af5b8a2d9..3449693fe521 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Entity.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Entity.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; /** @@ -93,7 +92,7 @@ public static Builder builder(Key key, FullEntity copyFrom) { return new Builder(key, copyFrom); } - static Entity fromPb(DatastoreV1.Entity entityPb) { + static Entity fromPb(com.google.datastore.v1beta3.Entity entityPb) { return new Builder().fill(entityPb).build(); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/EntityQuery.java similarity index 90% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/EntityQuery.java index 902168f20f48..40249b7ff26b 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/EntityQuery.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/EntityQuery.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1; +package com.google.cloud.datastore; /** * An implementation of a Google Cloud Datastore entity query that can be constructed by providing @@ -43,10 +41,10 @@ public static final class Builder extends StructuredQuery.BuilderImpl> { @@ -41,12 +38,12 @@ public Builder newBuilder(FullEntity value) { } @Override - protected FullEntity getValue(DatastoreV1.Value from) { + protected FullEntity getValue(com.google.datastore.v1beta3.Value from) { return FullEntity.fromPb(from.getEntityValue()); } @Override - protected void setValue(EntityValue from, DatastoreV1.Value.Builder to) { + protected void setValue(EntityValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setEntityValue(from.get().toPb()); } }; @@ -57,13 +54,6 @@ private Builder() { super(ValueType.ENTITY); } - @Override - public Builder indexed(boolean indexed) { - // see issue #25 - Preconditions.checkArgument(!indexed, "EntityValue can't be indexed"); - return super.indexed(indexed); - } - @Override public EntityValue build() { return new EntityValue(this); @@ -88,6 +78,6 @@ public static EntityValue of(FullEntity entity) { } public static Builder builder(FullEntity entity) { - return new Builder().set(entity).indexed(false); + return new Builder().set(entity); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/FullEntity.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/FullEntity.java similarity index 92% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/FullEntity.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/FullEntity.java index 55c573b9a636..aa88a92d3554 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/FullEntity.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/FullEntity.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1; +package com.google.cloud.datastore; /** * A full entity is a {@link BaseEntity} that holds all the properties associated with a @@ -71,7 +69,7 @@ public static Builder builder(FullEntity copyFro } - static FullEntity fromPb(DatastoreV1.Entity entityPb) { + static FullEntity fromPb(com.google.datastore.v1beta3.Entity entityPb) { return new Builder<>().fill(entityPb).build(); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/GqlQuery.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/GqlQuery.java similarity index 71% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/GqlQuery.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/GqlQuery.java index 7c03b69d9f39..2af3559f6899 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/GqlQuery.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/GqlQuery.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; +import static com.google.cloud.datastore.Validator.validateNamespace; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.datastore.Validator.validateNamespace; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -75,25 +74,22 @@ public final class GqlQuery extends Query { private final transient String queryString; private final transient boolean allowLiteral; - private final transient ImmutableList namedBindings; + private final transient ImmutableMap namedBindings; private final transient ImmutableList positionalBindings; - static final class Binding extends Serializable { + static final class Binding extends Serializable { private static final long serialVersionUID = 1976895435257636275L; - private final transient String name; private final transient Cursor cursor; private final transient Value value; - Binding(String name, Cursor cursor) { - this.name = name; + Binding(Cursor cursor) { this.cursor = checkNotNull(cursor); value = null; } - Binding(String name, Value value) { - this.name = name; + Binding(Value value) { this.value = checkNotNull(value); cursor = null; } @@ -102,13 +98,9 @@ Object cursorOrValue() { return MoreObjects.firstNonNull(cursor, value); } - String name() { - return name; - } - @Override public int hashCode() { - return Objects.hash(name, cursor, value); + return Objects.hash(cursor, value); } @Override @@ -120,17 +112,13 @@ public boolean equals(Object obj) { return false; } Binding other = (Binding) obj; - return Objects.equals(name, other.name) - && Objects.equals(cursor, other.cursor) - && Objects.equals(value, other.value); + return Objects.equals(cursor, other.cursor) && Objects.equals(value, other.value); } @Override - DatastoreV1.GqlQueryArg toPb() { - DatastoreV1.GqlQueryArg.Builder argPb = DatastoreV1.GqlQueryArg.newBuilder(); - if (name != null) { - argPb.setName(name); - } + com.google.datastore.v1beta3.GqlQueryParameter toPb() { + com.google.datastore.v1beta3.GqlQueryParameter.Builder argPb = + com.google.datastore.v1beta3.GqlQueryParameter.newBuilder(); if (cursor != null) { argPb.setCursor(cursor.byteString()); } @@ -142,15 +130,18 @@ DatastoreV1.GqlQueryArg toPb() { @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.GqlQueryArg.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.GqlQueryParameter.parseFrom(bytesPb)); } - static Binding fromPb(DatastoreV1.GqlQueryArg argPb) { - String name = argPb.hasName() ? argPb.getName() : null; - if (argPb.hasCursor()) { - return new Binding(name, new Cursor(argPb.getCursor())); + static Binding fromPb(com.google.datastore.v1beta3.GqlQueryParameter argPb) { + switch (argPb.getParameterTypeCase()) { + case CURSOR: + return new Binding(new Cursor(argPb.getCursor())); + case VALUE: + return new Binding(Value.fromPb(argPb.getValue())); + default: + throw new AssertionError("Unexpected enum value " + argPb.getParameterTypeCase()); } - return new Binding(name, Value.fromPb(argPb.getValue())); } } @@ -193,52 +184,52 @@ public Builder clearBindings() { } public Builder setBinding(String name, Cursor cursor) { - namedBindings.put(name, new Binding(name, cursor)); + namedBindings.put(name, new Binding(cursor)); return this; } public Builder setBinding(String name, String... value) { - namedBindings.put(name, toBinding(name, StringValue.MARSHALLER, Arrays.asList(value))); + namedBindings.put(name, toBinding(StringValue.MARSHALLER, Arrays.asList(value))); return this; } public Builder setBinding(String name, long... value) { - namedBindings.put(name, toBinding(name, LongValue.MARSHALLER, Longs.asList(value))); + namedBindings.put(name, toBinding(LongValue.MARSHALLER, Longs.asList(value))); return this; } public Builder setBinding(String name, double... value) { - namedBindings.put(name, toBinding(name, DoubleValue.MARSHALLER, Doubles.asList(value))); + namedBindings.put(name, toBinding(DoubleValue.MARSHALLER, Doubles.asList(value))); return this; } public Builder setBinding(String name, boolean... value) { - namedBindings.put(name, toBinding(name, BooleanValue.MARSHALLER, Booleans.asList(value))); + namedBindings.put(name, toBinding(BooleanValue.MARSHALLER, Booleans.asList(value))); return this; } public Builder setBinding(String name, DateTime... value) { - namedBindings.put(name, toBinding(name, DateTimeValue.MARSHALLER, Arrays.asList(value))); + namedBindings.put(name, toBinding(DateTimeValue.MARSHALLER, Arrays.asList(value))); return this; } public Builder setBinding(String name, Key... value) { - namedBindings.put(name, toBinding(name, KeyValue.MARSHALLER, Arrays.asList(value))); + namedBindings.put(name, toBinding(KeyValue.MARSHALLER, Arrays.asList(value))); return this; } public Builder setBinding(String name, FullEntity... value) { - namedBindings.put(name, toBinding(name, EntityValue.MARSHALLER, Arrays.asList(value))); + namedBindings.put(name, toBinding(EntityValue.MARSHALLER, Arrays.asList(value))); return this; } public Builder setBinding(String name, Blob... value) { - namedBindings.put(name, toBinding(name, BlobValue.MARSHALLER, Arrays.asList(value))); + namedBindings.put(name, toBinding(BlobValue.MARSHALLER, Arrays.asList(value))); return this; } public Builder addBinding(Cursor cursor) { - positionalBindings.add(new Binding(null, cursor)); + positionalBindings.add(new Binding(cursor)); return this; } @@ -286,11 +277,7 @@ public GqlQuery build() { return new GqlQuery<>(this); } - private static Binding toBinding(Value.BuilderFactory builderFactory, List values) { - return toBinding(null, builderFactory, values); - } - - private static Binding toBinding(String name, Value.BuilderFactory builderFactory, + private static Binding toBinding(Value.BuilderFactory builderFactory, List values) { List> list = new ArrayList<>(values.size()); for (Object object : values) { @@ -306,7 +293,7 @@ private static Binding toBinding(String name, Value.BuilderFactory } else { value = new ListValue(list); } - return new Binding(name, value); + return new Binding(value); } } @@ -314,7 +301,7 @@ private GqlQuery(Builder builder) { super(builder.resultType, builder.namespace); queryString = builder.queryString; allowLiteral = builder.allowLiteral; - namedBindings = ImmutableList.copyOf(builder.namedBindings.values()); + namedBindings = ImmutableMap.copyOf(builder.namedBindings); positionalBindings = ImmutableList.copyOf(builder.positionalBindings); } @@ -331,8 +318,8 @@ public boolean allowLiteral() { */ public Map namedBindings() { ImmutableMap.Builder builder = ImmutableSortedMap.naturalOrder(); - for (Binding binding : namedBindings) { - builder.put(binding.name(), binding.cursorOrValue()); + for (Map.Entry binding : namedBindings.entrySet()) { + builder.put(binding.getKey(), binding.getValue().cursorOrValue()); } return builder.build(); } @@ -370,50 +357,53 @@ public boolean equals(Object obj) { } @Override - DatastoreV1.GqlQuery toPb() { - DatastoreV1.GqlQuery.Builder queryPb = DatastoreV1.GqlQuery.newBuilder(); + com.google.datastore.v1beta3.GqlQuery toPb() { + com.google.datastore.v1beta3.GqlQuery.Builder queryPb = + com.google.datastore.v1beta3.GqlQuery.newBuilder(); queryPb.setQueryString(queryString); - queryPb.setAllowLiteral(allowLiteral); - for (Binding argument : namedBindings) { - queryPb.addNameArg(argument.toPb()); + queryPb.setAllowLiterals(allowLiteral); + Map namedBindingsPb = + queryPb.getMutableNamedBindings(); + for (Map.Entry entry : namedBindings.entrySet()) { + namedBindingsPb.put(entry.getKey(), entry.getValue().toPb()); } for (Binding argument : positionalBindings) { - queryPb.addNumberArg(argument.toPb()); + queryPb.addPositionalBindings(argument.toPb()); } return queryPb.build(); } @Override - void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { + void populatePb(com.google.datastore.v1beta3.RunQueryRequest.Builder requestPb) { requestPb.setGqlQuery(toPb()); } @Override - GqlQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { - // See issue #17 - throw new UnsupportedOperationException("paging for this query is not implemented yet"); + Query nextQuery(com.google.datastore.v1beta3.RunQueryResponse responsePb) { + return StructuredQuery.fromPb(type(), namespace(), responsePb.getQuery()) + .nextQuery(responsePb); } @Override Object fromPb(ResultType resultType, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(resultType, namespace, DatastoreV1.GqlQuery.parseFrom(bytesPb)); + return fromPb(resultType, namespace, com.google.datastore.v1beta3.GqlQuery.parseFrom(bytesPb)); } private static GqlQuery fromPb( - ResultType resultType, String ns, DatastoreV1.GqlQuery queryPb) { + ResultType resultType, String ns, com.google.datastore.v1beta3.GqlQuery queryPb) { Builder builder = new Builder<>(resultType, queryPb.getQueryString()); builder.namespace(ns); - if (queryPb.hasAllowLiteral()) { - builder.allowLiteral = queryPb.getAllowLiteral(); - } - for (DatastoreV1.GqlQueryArg nameArg : queryPb.getNameArgList()) { - Binding argument = Binding.fromPb(nameArg); - builder.namedBindings.put(argument.name(), argument); - } - for (DatastoreV1.GqlQueryArg numberArg : queryPb.getNumberArgList()) { - Binding argument = Binding.fromPb(numberArg); - builder.positionalBindings.add(argument); + builder.allowLiteral = queryPb.getAllowLiterals(); + for (Map.Entry nameArg + : queryPb.getNamedBindings().entrySet()) { + Binding currBinding = Binding.fromPb(nameArg.getValue()); + builder.namedBindings.put(nameArg.getKey(), currBinding); + } + for (com.google.datastore.v1beta3.GqlQueryParameter numberArg + : queryPb.getPositionalBindingsList()) { + Binding currBinding = Binding.fromPb(numberArg); + builder.positionalBindings.add(currBinding); } return builder.build(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/IncompleteKey.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/IncompleteKey.java similarity index 82% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/IncompleteKey.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/IncompleteKey.java index 2192384ef70b..18959005596c 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/IncompleteKey.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/IncompleteKey.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; @@ -55,25 +54,21 @@ public IncompleteKey build() { @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.Key.parseFrom(bytesPb)); } - static IncompleteKey fromPb(DatastoreV1.Key keyPb) { - String projectId = null; - String namespace = null; + static IncompleteKey fromPb(com.google.datastore.v1beta3.Key keyPb) { + String projectId = ""; + String namespace = ""; if (keyPb.hasPartitionId()) { - DatastoreV1.PartitionId partitionIdPb = keyPb.getPartitionId(); - if (partitionIdPb.hasDatasetId()) { - projectId = partitionIdPb.getDatasetId(); - } - if (partitionIdPb.hasNamespace()) { - namespace = partitionIdPb.getNamespace(); - } + com.google.datastore.v1beta3.PartitionId partitionIdPb = keyPb.getPartitionId(); + projectId = partitionIdPb.getProjectId(); + namespace = partitionIdPb.getNamespaceId(); } - List pathElementsPb = keyPb.getPathElementList(); + List pathElementsPb = keyPb.getPathList(); Preconditions.checkArgument(!pathElementsPb.isEmpty(), "Path must not be empty"); ImmutableList.Builder pathBuilder = ImmutableList.builder(); - for (DatastoreV1.Key.PathElement pathElementPb : pathElementsPb) { + for (com.google.datastore.v1beta3.Key.PathElement pathElementPb : pathElementsPb) { pathBuilder.add(PathElement.fromPb(pathElementPb)); } ImmutableList path = pathBuilder.build(); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Key.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Key.java similarity index 95% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Key.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Key.java index c6cdc0fa6142..7cb70d259e82 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Key.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Key.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; @@ -153,7 +152,8 @@ public String toUrlSafe() { public static Key fromUrlSafe(String urlSafe) { try { String utf8Str = URLDecoder.decode(urlSafe, UTF_8.name()); - DatastoreV1.Key.Builder builder = DatastoreV1.Key.newBuilder(); + com.google.datastore.v1beta3.Key.Builder builder = + com.google.datastore.v1beta3.Key.newBuilder(); TextFormat.merge(utf8Str, builder); return fromPb(builder.build()); } catch (UnsupportedEncodingException e) { @@ -165,10 +165,10 @@ public static Key fromUrlSafe(String urlSafe) { @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Key.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.Key.parseFrom(bytesPb)); } - static Key fromPb(DatastoreV1.Key keyPb) { + static Key fromPb(com.google.datastore.v1beta3.Key keyPb) { IncompleteKey key = IncompleteKey.fromPb(keyPb); Preconditions.checkState(key instanceof Key, "Key is not complete"); return (Key) key; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyFactory.java similarity index 97% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyFactory.java index a440992870df..f7fc17448a6d 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyFactory.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import com.google.common.collect.ImmutableList; @@ -28,7 +28,7 @@ public final class KeyFactory extends BaseKey.Builder { private final String ns; public KeyFactory(String projectId) { - this(projectId, null); + this(projectId, ""); } public KeyFactory(String projectId, String namespace) { diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyQuery.java similarity index 84% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyQuery.java index 7afa0f5099d6..d84dd5b26c20 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/KeyQuery.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/KeyQuery.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; - -import com.google.api.services.datastore.DatastoreV1; +package com.google.cloud.datastore; /** * An implementation of a Google Cloud Datastore key-only query that can be constructed by providing @@ -40,14 +38,14 @@ public static final class Builder extends StructuredQuery.BuilderImpl { @@ -40,12 +38,12 @@ public Builder newBuilder(Key key) { } @Override - protected Key getValue(DatastoreV1.Value from) { + protected Key getValue(com.google.datastore.v1beta3.Value from) { return Key.fromPb(from.getKeyValue()); } @Override - protected void setValue(KeyValue from, DatastoreV1.Value.Builder to) { + protected void setValue(KeyValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setKeyValue(from.get().toPb()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLng.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLng.java new file mode 100644 index 000000000000..72f9e9f4f04f --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLng.java @@ -0,0 +1,90 @@ +/* + * 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.cloud.datastore; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.protobuf.InvalidProtocolBufferException; + +import java.util.Objects; + +/** + * A Google Cloud Datastore LatLng (represented by latitude and longitude in degrees). + * This class is immutable. + * + * @see Google Cloud Datastore + * Entities, Properties, and Keys + */ +public final class LatLng extends Serializable { + + private static final long serialVersionUID = 9077060962655752073L; + + private final transient double latitude; + private final transient double longitude; + + LatLng(double latitude, double longitude) { + checkArgument( + latitude >= -90.0 && latitude <= 90.0, "latitude must be in the range [-90, 90] degrees"); + checkArgument( + longitude >= -180.0 && longitude <= 180.0, + "latitude must be in the range [-180, 180] degrees"); + this.latitude = latitude; + this.longitude = longitude; + } + + public double latitude() { + return latitude; + } + + public double longitude() { + return longitude; + } + + @Override + public String toString() { + return Double.toString(latitude) + ", " + Double.toString(longitude); + } + + @Override + public int hashCode() { + return Objects.hash(latitude, longitude); + } + + @Override + public boolean equals(Object obj) { + return obj == this || (obj instanceof LatLng && this.latitude == ((LatLng) obj).latitude + && this.longitude == ((LatLng) obj).longitude); + } + + public static LatLng of(double latitude, double longitude) { + return new LatLng(latitude, longitude); + } + + @Override + protected com.google.type.LatLng toPb() { + return com.google.type.LatLng.newBuilder() + .setLatitude(latitude) + .setLongitude(longitude) + .build(); + } + + @Override + protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { + com.google.type.LatLng parsedLatLng = com.google.type.LatLng.parseFrom(bytesPb); + return new LatLng(parsedLatLng.getLatitude(), parsedLatLng.getLongitude()); + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLngValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLngValue.java new file mode 100644 index 000000000000..80faa427cf28 --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LatLngValue.java @@ -0,0 +1,84 @@ +/* + * 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.cloud.datastore; + +import static com.google.datastore.v1beta3.Value.GEO_POINT_VALUE_FIELD_NUMBER; + +public final class LatLngValue extends Value { + + private static final long serialVersionUID = -5810614280642405898L; + + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { + + private static final long serialVersionUID = -3550567536035178649L; + + @Override + public int getProtoFieldId() { + return GEO_POINT_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(LatLng value) { + return builder(value); + } + + @Override + protected LatLng getValue(com.google.datastore.v1beta3.Value from) { + return new LatLng( + from.getGeoPointValue().getLatitude(), from.getGeoPointValue().getLongitude()); + } + + @Override + protected void setValue(LatLngValue from, com.google.datastore.v1beta3.Value.Builder to) { + to.setGeoPointValue(from.get().toPb()); + } + }; + + public static final class Builder extends Value.BaseBuilder { + + private Builder() { + super(ValueType.LAT_LNG); + } + + @Override + public LatLngValue build() { + return new LatLngValue(this); + } + } + + public LatLngValue(LatLng value) { + this(builder(value)); + } + + private LatLngValue(Builder builder) { + super(builder); + } + + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + + public static LatLngValue of(LatLng value) { + return new LatLngValue(value); + } + + public static Builder builder(LatLng value) { + return new Builder().set(value); + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ListValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ListValue.java new file mode 100644 index 000000000000..d43b78053df7 --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ListValue.java @@ -0,0 +1,327 @@ +/* + * 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.cloud.datastore; + +import static com.google.datastore.v1beta3.Value.ARRAY_VALUE_FIELD_NUMBER; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.List; + +/** + * A Google Cloud Datastore list value. A list value is a list of {@link Value} objects. + */ +public final class ListValue extends Value>> { + + private static final long serialVersionUID = -5461475706792576395L; + + static final BaseMarshaller>, ListValue, Builder> MARSHALLER = + new BaseMarshaller>, ListValue, Builder>() { + + private static final long serialVersionUID = -3193794036327640106L; + + @Override + public int getProtoFieldId() { + return ARRAY_VALUE_FIELD_NUMBER; + } + + @Override + public Builder newBuilder(List> values) { + return builder().set(values); + } + + @Override + protected List> getValue(com.google.datastore.v1beta3.Value from) { + List> properties = new ArrayList<>(from.getArrayValue().getValuesCount()); + for (com.google.datastore.v1beta3.Value valuePb : from.getArrayValue().getValuesList()) { + properties.add(Value.fromPb(valuePb)); + } + return properties; + } + + @Override + protected void setValue(ListValue from, com.google.datastore.v1beta3.Value.Builder to) { + List propertiesPb = + new ArrayList(); + for (Value property : from.get()) { + propertiesPb.add(property.toPb()); + } + to.setArrayValue(com.google.datastore.v1beta3.ArrayValue.newBuilder() + .addAllValues(propertiesPb)); + } + }; + + public static final class Builder extends + Value.BaseBuilder>, ListValue, Builder> { + + private ImmutableList.Builder> listBuilder = ImmutableList.builder(); + + private Builder() { + super(ValueType.LIST); + } + + private void addValueHelper(Value value) { + // see datastore.proto definition for list_value + Preconditions.checkArgument(value.type() != ValueType.LIST, "Cannot contain another list"); + listBuilder.add(value); + } + + /** + * Adds the provided values to the {@code ListValue} builder. + */ + public Builder addValue(Value first, Value... other) { + addValueHelper(first); + for (Value value : other) { + addValueHelper(value); + } + return this; + } + + /** + * Adds the provided string values to the {@code ListValue} builder. + */ + public Builder addValue(String first, String... other) { + listBuilder.add(StringValue.of(first)); + for (String value : other) { + listBuilder.add(StringValue.of(value)); + } + return this; + } + + /** + * Adds the provided long values to the {@code ListValue} builder. + */ + public Builder addValue(long first, long... other) { + listBuilder.add(LongValue.of(first)); + for (long value : other) { + listBuilder.add(LongValue.of(value)); + } + return this; + } + + /** + * Adds the provided double values to the {@code ListValue} builder. + */ + public Builder addValue(double first, double... other) { + listBuilder.add(DoubleValue.of(first)); + for (double value : other) { + listBuilder.add(DoubleValue.of(value)); + } + return this; + } + + /** + * Adds the provided boolean values to the {@code ListValue} builder. + */ + public Builder addValue(boolean first, boolean... other) { + listBuilder.add(BooleanValue.of(first)); + for (boolean value : other) { + listBuilder.add(BooleanValue.of(value)); + } + return this; + } + + /** + * Adds the provided {@code DateTime} values to the {@code ListValue} builder. + */ + public Builder addValue(DateTime first, DateTime... other) { + listBuilder.add(DateTimeValue.of(first)); + for (DateTime value : other) { + listBuilder.add(DateTimeValue.of(value)); + } + return this; + } + + /** + * Adds the provided {@code LatLng} values to the {@code ListValue} builder. + */ + public Builder addValue(LatLng first, LatLng... other) { + listBuilder.add(LatLngValue.of(first)); + for (LatLng value : other) { + listBuilder.add(LatLngValue.of(value)); + } + return this; + } + + /** + * Adds the provided {@code Key} values to the {@code ListValue} builder. + */ + public Builder addValue(Key first, Key... other) { + listBuilder.add(KeyValue.of(first)); + for (Key value : other) { + listBuilder.add(KeyValue.of(value)); + } + return this; + } + + /** + * Adds the provided {@code FullEntity} values to the {@code ListValue} builder. + */ + public Builder addValue(FullEntity first, FullEntity... other) { + listBuilder.add(EntityValue.of(first)); + for (FullEntity value : other) { + listBuilder.add(EntityValue.of(value)); + } + return this; + } + + /** + * Adds the provided {@code Blob} values to the {@code ListValue} builder. + */ + public Builder addValue(Blob first, Blob... other) { + listBuilder.add(BlobValue.of(first)); + for (Blob value : other) { + listBuilder.add(BlobValue.of(value)); + } + return this; + } + + /** + * Sets the list of values of this {@code ListValue} builder to {@code values}. The provided + * list is copied. + * + * @see com.google.cloud.datastore.Value.BaseBuilder#set(java.lang.Object) + */ + @Override + public Builder set(List> values) { + listBuilder = ImmutableList.builder(); + for (Value value : values) { + addValue(value); + } + return this; + } + + @Override + public List> get() { + return listBuilder.build(); + } + + /** + * Creates a {@code ListValue} object. + */ + @Override + public ListValue build() { + return new ListValue(this); + } + } + + public ListValue(List> values) { + this(builder().set(values)); + } + + public ListValue(Value first, Value... other) { + this(new Builder().addValue(first, other)); + } + + private ListValue(Builder builder) { + super(builder); + } + + /** + * Returns a builder for the list value object. + */ + @Override + public Builder toBuilder() { + return new Builder().mergeFrom(this); + } + + /** + * Creates a {@code ListValue} object given a list of {@code Value} objects. + */ + public static ListValue of(List> values) { + return new ListValue(values); + } + + /** + * Creates a {@code ListValue} object given a number of {@code Value} objects. + */ + public static ListValue of(Value first, Value... other) { + return new ListValue(first, other); + } + + /** + * Creates a {@code ListValue} object given a number of string values. + */ + public static ListValue of(String first, String... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of long values. + */ + public static ListValue of(long first, long... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of double values. + */ + public static ListValue of(double first, double... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of boolean values. + */ + public static ListValue of(boolean first, boolean... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of {@code DateTime} values. + */ + public static ListValue of(DateTime first, DateTime... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of {@code LatLng} values. + */ + public static ListValue of(LatLng first, LatLng... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of {@code Key} values. + */ + public static ListValue of(Key first, Key... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of {@code FullEntity} values. + */ + public static ListValue of(FullEntity first, FullEntity... other) { + return builder().addValue(first, other).build(); + } + + /** + * Creates a {@code ListValue} object given a number of {@code Blob} values. + */ + public static ListValue of(Blob first, Blob... other) { + return builder().addValue(first, other).build(); + } + + /** + * Returns a builder for {@code ListValue} objects. + */ + public static Builder builder() { + return new Builder(); + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/LongValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LongValue.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/LongValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LongValue.java index 43d139e90249..ba79af7fb233 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/LongValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/LongValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.INTEGER_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.INTEGER_VALUE_FIELD_NUMBER; public final class LongValue extends Value { @@ -40,12 +38,12 @@ public Builder newBuilder(Long value) { } @Override - protected Long getValue(DatastoreV1.Value from) { + protected Long getValue(com.google.datastore.v1beta3.Value from) { return from.getIntegerValue(); } @Override - protected void setValue(LongValue from, DatastoreV1.Value.Builder to) { + protected void setValue(LongValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setIntegerValue(from.get()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/NullValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/NullValue.java similarity index 79% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/NullValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/NullValue.java index 58fed152ffd5..4721e572215f 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/NullValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/NullValue.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkArgument; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.NULL_VALUE_FIELD_NUMBER; public final class NullValue extends Value { @@ -36,21 +35,22 @@ public Builder newBuilder(Void value) { @Override public int getProtoFieldId() { - return 0; + return NULL_VALUE_FIELD_NUMBER; } @Override - protected Void getValue(DatastoreV1.Value from) { + protected Void getValue(com.google.datastore.v1beta3.Value from) { return null; } @Override - protected void setValue(NullValue from, DatastoreV1.Value.Builder to) { - // nothing to set + protected void setValue(NullValue from, com.google.datastore.v1beta3.Value.Builder to) { + to.setNullValue(com.google.protobuf.NullValue.NULL_VALUE); } }; - public static final class Builder extends Value.BaseBuilder { + public static final class Builder + extends Value.BaseBuilder { private Builder() { super(ValueType.NULL); diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/PathElement.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/PathElement.java similarity index 80% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/PathElement.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/PathElement.java index 6b76eb70ea5b..413b0786e021 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/PathElement.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/PathElement.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.Strings; import com.google.protobuf.InvalidProtocolBufferException; @@ -28,7 +27,7 @@ /** * Represents a single element in a key's path. */ -public final class PathElement extends Serializable { +public final class PathElement extends Serializable { private static final long serialVersionUID = -7968078857690784595L; @@ -86,8 +85,9 @@ public boolean equals(Object obj) { } @Override - DatastoreV1.Key.PathElement toPb() { - DatastoreV1.Key.PathElement.Builder pathElementPb = DatastoreV1.Key.PathElement.newBuilder(); + com.google.datastore.v1beta3.Key.PathElement toPb() { + com.google.datastore.v1beta3.Key.PathElement.Builder pathElementPb = + com.google.datastore.v1beta3.Key.PathElement.newBuilder(); pathElementPb.setKind(kind); if (id != null) { pathElementPb.setId(id); @@ -99,18 +99,19 @@ DatastoreV1.Key.PathElement toPb() { @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Key.PathElement.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.Key.PathElement.parseFrom(bytesPb)); } - static PathElement fromPb(DatastoreV1.Key.PathElement pathElementPb) { + static PathElement fromPb(com.google.datastore.v1beta3.Key.PathElement pathElementPb) { String kind = pathElementPb.getKind(); - if (pathElementPb.hasId()) { - return of(kind, pathElementPb.getId()); + switch (pathElementPb.getIdTypeCase()) { + case ID: + return of(kind, pathElementPb.getId()); + case NAME: + return of(kind, pathElementPb.getName()); + default: + return of(kind); } - if (pathElementPb.hasName()) { - return of(kind, pathElementPb.getName()); - } - return of(kind); } static PathElement of(String kind) { diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntity.java similarity index 87% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntity.java index 1ba054b68161..519262a2e9c6 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntity.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntity.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; import com.google.protobuf.ByteString; /** @@ -56,7 +55,7 @@ public ProjectionEntity build() { @Override public DateTime getDateTime(String name) { Value value = getValue(name); - if (value.hasMeaning() && value.meaning() == 18 && value instanceof LongValue) { + if (value.meaning() == 18 && value instanceof LongValue) { return new DateTime(getLong(name)); } return ((Value) value).get(); @@ -66,13 +65,13 @@ public DateTime getDateTime(String name) { @Override public Blob getBlob(String name) { Value value = getValue(name); - if (value.hasMeaning() && value.meaning() == 18 && value instanceof StringValue) { + if (value.meaning() == 18 && value instanceof StringValue) { return new Blob(ByteString.copyFromUtf8(getString(name))); } return ((Value) value).get(); } - static ProjectionEntity fromPb(DatastoreV1.Entity entityPb) { + static ProjectionEntity fromPb(com.google.datastore.v1beta3.Entity entityPb) { return new Builder().fill(entityPb).build(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntityQuery.java similarity index 84% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntityQuery.java index bad9fc5af2d0..5ccfed549941 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ProjectionEntityQuery.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ProjectionEntityQuery.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; /** * An implementation of a Google Cloud Datastore projection entity query that can be constructed by @@ -54,7 +54,7 @@ public Builder clearProjection() { * Sets the query's projection clause (clearing any previously specified Projection settings). */ @Override - public Builder projection(Projection projection, Projection... others) { + public Builder projection(String projection, String... others) { super.projection(projection, others); return this; } @@ -63,7 +63,7 @@ public Builder projection(Projection projection, Projection... others) { * Adds one or more projections to the existing projection clause. */ @Override - public Builder addProjection(Projection projection, Projection... others) { + public Builder addProjection(String projection, String... others) { super.addProjection(projection, others); return this; } @@ -72,8 +72,8 @@ public Builder addProjection(Projection projection, Projection... others) { * Clears the group by clause. */ @Override - public Builder clearGroupBy() { - super.clearGroupBy(); + public Builder clearDistinctOn() { + super.clearDistinctOn(); return this; } @@ -81,8 +81,8 @@ public Builder clearGroupBy() { * Sets the query's group by clause (clearing any previously specified GroupBy settings). */ @Override - public Builder groupBy(String property, String... others) { - super.groupBy(property, others); + public Builder distinctOn(String property, String... others) { + super.distinctOn(property, others); return this; } @@ -90,8 +90,8 @@ public Builder groupBy(String property, String... others) { * Adds one or more properties to the existing group by clause. */ @Override - public Builder addGroupBy(String property, String... others) { - super.addGroupBy(property, others); + public Builder addDistinctOn(String property, String... others) { + super.addDistinctOn(property, others); return this; } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Query.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Query.java similarity index 71% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Query.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Query.java index 50591a87a6a4..c37e784d04ee 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Query.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Query.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.collect.Maps; @@ -55,63 +54,69 @@ public abstract class Query extends Serializable { public abstract static class ResultType implements java.io.Serializable { private static final long serialVersionUID = 2104157695425806623L; - private static final Map> - PB_TO_INSTANCE = Maps.newEnumMap(DatastoreV1.EntityResult.ResultType.class); + private static final Map> + PB_TO_INSTANCE = Maps.newEnumMap( + com.google.datastore.v1beta3.EntityResult.ResultType.class); - static final ResultType UNKNOWN = new ResultType(null, Object.class) { + static final ResultType UNKNOWN = + new ResultType(null, Object.class) { - private static final long serialVersionUID = 1602329532153860907L; + private static final long serialVersionUID = 1602329532153860907L; - @Override - Object convert(DatastoreV1.Entity entityPb) { - if (entityPb.getPropertyCount() == 0) { - if (!entityPb.hasKey()) { - return null; + @Override + Object convert(com.google.datastore.v1beta3.Entity entityPb) { + if (entityPb.getProperties().isEmpty()) { + if (!entityPb.hasKey()) { + return null; + } + return Key.fromPb(entityPb.getKey()); + } + return ProjectionEntity.fromPb(entityPb); } - return Key.fromPb(entityPb.getKey()); - } - return ProjectionEntity.fromPb(entityPb); - } - }; + }; public static final ResultType ENTITY = - new ResultType(DatastoreV1.EntityResult.ResultType.FULL, Entity.class) { + new ResultType( + com.google.datastore.v1beta3.EntityResult.ResultType.FULL, Entity.class) { - private static final long serialVersionUID = 7712959777507168274L; + private static final long serialVersionUID = 7712959777507168274L; - @Override - Entity convert(DatastoreV1.Entity entityPb) { - return Entity.fromPb(entityPb); - } - }; + @Override + Entity convert(com.google.datastore.v1beta3.Entity entityPb) { + return Entity.fromPb(entityPb); + } + }; public static final ResultType KEY = - new ResultType(DatastoreV1.EntityResult.ResultType.KEY_ONLY, Key.class) { + new ResultType( + com.google.datastore.v1beta3.EntityResult.ResultType.KEY_ONLY, Key.class) { - private static final long serialVersionUID = -8514289244104446252L; + private static final long serialVersionUID = -8514289244104446252L; - @Override - Key convert(DatastoreV1.Entity entityPb) { - return Key.fromPb(entityPb.getKey()); - } - }; + @Override + Key convert(com.google.datastore.v1beta3.Entity entityPb) { + return Key.fromPb(entityPb.getKey()); + } + }; public static final ResultType PROJECTION_ENTITY = - new ResultType(DatastoreV1.EntityResult.ResultType.PROJECTION, + new ResultType( + com.google.datastore.v1beta3.EntityResult.ResultType.PROJECTION, ProjectionEntity.class) { private static final long serialVersionUID = -7591409419690650246L; @Override - ProjectionEntity convert(DatastoreV1.Entity entityPb) { + ProjectionEntity convert(com.google.datastore.v1beta3.Entity entityPb) { return ProjectionEntity.fromPb(entityPb); } - }; + }; private final Class resultClass; - private final DatastoreV1.EntityResult.ResultType queryType; + private final com.google.datastore.v1beta3.EntityResult.ResultType queryType; - private ResultType(DatastoreV1.EntityResult.ResultType queryType, Class resultClass) { + private ResultType(com.google.datastore.v1beta3.EntityResult.ResultType queryType, + Class resultClass) { this.queryType = queryType; this.resultClass = resultClass; if (queryType != null) { @@ -152,9 +157,9 @@ boolean isAssignableFrom(ResultType otherResultType) { return resultClass.isAssignableFrom(otherResultType.resultClass); } - abstract V convert(DatastoreV1.Entity entityPb); + abstract V convert(com.google.datastore.v1beta3.Entity entityPb); - static ResultType fromPb(DatastoreV1.EntityResult.ResultType typePb) { + static ResultType fromPb(com.google.datastore.v1beta3.EntityResult.ResultType typePb) { return MoreObjects.firstNonNull(PB_TO_INSTANCE.get(typePb), UNKNOWN); } } @@ -189,9 +194,9 @@ Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { abstract Object fromPb(ResultType resultType, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException; - abstract void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb); + abstract void populatePb(com.google.datastore.v1beta3.RunQueryRequest.Builder requestPb); - abstract Query nextQuery(DatastoreV1.QueryResultBatch responsePb); + abstract Query nextQuery(com.google.datastore.v1beta3.RunQueryResponse responsePb); /** * Returns a new {@link GqlQuery} builder. diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResults.java similarity index 65% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResults.java index b882131ba939..a907ddcad6b9 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResults.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResults.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.Iterator; @@ -36,8 +36,21 @@ public interface QueryResults extends Iterator { Class resultClass(); /** - * Returns the Cursor for the point after the value returned in the last {@link #next} call. - * Currently, {@code cursorAfter} returns null in all cases but the last result. + * Returns the Cursor for the point after the value returned in the last {@link #next} call. This + * cursor can be used to issue subsequent queries (with the same constraints) that may return + * additional results. + * + *

A simple use case: + *

 {@code
+   * Query query = Query.entityQueryBuilder()
+   *     .kind("Person")
+   *     .filter(PropertyFilter.eq("favoriteFood", "pizza"))
+   *     .build();
+   * QueryResults results = datastore.run(query);
+   * // Consume some results (using results.next()) and do any other actions as necessary.
+   * query = query.toBuilder().startCursor(results.cursorAfter()).build();
+   * results = datastore.run(query); // now we will iterate over all entities not yet consumed
+   * }
*/ Cursor cursorAfter(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResultsImpl.java similarity index 54% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResultsImpl.java index 3c2e0d177f80..f049ef9edde2 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/QueryResultsImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/QueryResultsImpl.java @@ -14,13 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.QueryResultBatch.MoreResultsType; +import com.google.cloud.datastore.Query.ResultType; import com.google.common.base.Preconditions; import com.google.common.collect.AbstractIterator; -import com.google.gcloud.datastore.Query.ResultType; +import com.google.datastore.v1beta3.QueryResultBatch.MoreResultsType; import com.google.protobuf.ByteString; import java.util.Iterator; @@ -29,46 +28,56 @@ class QueryResultsImpl extends AbstractIterator implements QueryResults { private final DatastoreImpl datastore; - private final DatastoreV1.ReadOptions readOptionsPb; - private final DatastoreV1.PartitionId partitionIdPb; + private final com.google.datastore.v1beta3.ReadOptions readOptionsPb; + private final com.google.datastore.v1beta3.PartitionId partitionIdPb; private final ResultType queryResultType; private Query query; private ResultType actualResultType; - private DatastoreV1.QueryResultBatch queryResultBatchPb; + private com.google.datastore.v1beta3.RunQueryResponse runQueryResponsePb; + private com.google.datastore.v1beta3.Query mostRecentQueryPb; private boolean lastBatch; - private Iterator entityResultPbIter; - private ByteString cursor; // only available in v1beta3 + private Iterator entityResultPbIter; + private ByteString cursor; - - QueryResultsImpl(DatastoreImpl datastore, DatastoreV1.ReadOptions readOptionsPb, + QueryResultsImpl(DatastoreImpl datastore, com.google.datastore.v1beta3.ReadOptions readOptionsPb, Query query) { this.datastore = datastore; this.readOptionsPb = readOptionsPb; this.query = query; queryResultType = query.type(); - DatastoreV1.PartitionId.Builder pbBuilder = DatastoreV1.PartitionId.newBuilder(); - pbBuilder.setDatasetId(datastore.options().projectId()); + com.google.datastore.v1beta3.PartitionId.Builder pbBuilder = + com.google.datastore.v1beta3.PartitionId.newBuilder(); + pbBuilder.setProjectId(datastore.options().projectId()); if (query.namespace() != null) { - pbBuilder.setNamespace(query.namespace()); + pbBuilder.setNamespaceId(query.namespace()); } else if (datastore.options().namespace() != null) { - pbBuilder.setNamespace(datastore.options().namespace()); + pbBuilder.setNamespaceId(datastore.options().namespace()); } partitionIdPb = pbBuilder.build(); sendRequest(); + if (runQueryResponsePb.getBatch().getSkippedResults() > 0) { + cursor = runQueryResponsePb.getBatch().getSkippedCursor(); + } else { + cursor = mostRecentQueryPb.getStartCursor(); + } } private void sendRequest() { - DatastoreV1.RunQueryRequest.Builder requestPb = DatastoreV1.RunQueryRequest.newBuilder(); + com.google.datastore.v1beta3.RunQueryRequest.Builder requestPb = + com.google.datastore.v1beta3.RunQueryRequest.newBuilder(); if (readOptionsPb != null) { requestPb.setReadOptions(readOptionsPb); } requestPb.setPartitionId(partitionIdPb); query.populatePb(requestPb); - queryResultBatchPb = datastore.runQuery(requestPb.build()).getBatch(); - lastBatch = queryResultBatchPb.getMoreResults() != MoreResultsType.NOT_FINISHED; - entityResultPbIter = queryResultBatchPb.getEntityResultList().iterator(); - // cursor = resultPb.getSkippedCursor(); // available in v1beta3, use startCursor if not skipped - actualResultType = ResultType.fromPb(queryResultBatchPb.getEntityResultType()); + runQueryResponsePb = datastore.runQuery(requestPb.build()); + mostRecentQueryPb = runQueryResponsePb.getQuery(); + if (mostRecentQueryPb == null) { + mostRecentQueryPb = requestPb.getQuery(); + } + lastBatch = runQueryResponsePb.getBatch().getMoreResults() != MoreResultsType.NOT_FINISHED; + entityResultPbIter = runQueryResponsePb.getBatch().getEntityResultsList().iterator(); + actualResultType = ResultType.fromPb(runQueryResponsePb.getBatch().getEntityResultType()); if (Objects.equals(queryResultType, ResultType.PROJECTION_ENTITY)) { // projection entity can represent all type of results actualResultType = ResultType.PROJECTION_ENTITY; @@ -80,15 +89,15 @@ private void sendRequest() { @Override protected T computeNext() { while (!entityResultPbIter.hasNext() && !lastBatch) { - query = query.nextQuery(queryResultBatchPb); + query = query.nextQuery(runQueryResponsePb); sendRequest(); } if (!entityResultPbIter.hasNext()) { - cursor = queryResultBatchPb.getEndCursor(); + cursor = runQueryResponsePb.getBatch().getEndCursor(); return endOfData(); } - DatastoreV1.EntityResult entityResultPb = entityResultPbIter.next(); - //cursor = entityResultPb.getCursor(); // only available in v1beta3 + com.google.datastore.v1beta3.EntityResult entityResultPb = entityResultPbIter.next(); + cursor = entityResultPb.getCursor(); @SuppressWarnings("unchecked") T result = (T) actualResultType.convert(entityResultPb.getEntity()); return result; @@ -101,7 +110,6 @@ public Class resultClass() { @Override public Cursor cursorAfter() { - return cursor == null ? null : new Cursor(cursor); - //return new Cursor(cursor); // only available in v1beta3 + return new Cursor(cursor); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/RawValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/RawValue.java similarity index 61% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/RawValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/RawValue.java index 9d447cf4289b..81fec851a6dd 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/RawValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/RawValue.java @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; - -public final class RawValue extends Value { +public final class RawValue extends Value { private static final long serialVersionUID = -3359604598651897941L; - static final BaseMarshaller MARSHALLER = - new BaseMarshaller() { + static final BaseMarshaller MARSHALLER = + new BaseMarshaller() { private static final long serialVersionUID = 5320642719486106244L; @Override - public Builder newBuilder(DatastoreV1.Value value) { + public Builder newBuilder(com.google.datastore.v1beta3.Value value) { return builder(value); } @@ -38,18 +36,19 @@ public int getProtoFieldId() { } @Override - protected DatastoreV1.Value getValue(DatastoreV1.Value from) { + protected com.google.datastore.v1beta3.Value getValue( + com.google.datastore.v1beta3.Value from) { return from; } @Override - protected void setValue(RawValue from, DatastoreV1.Value.Builder to) { + protected void setValue(RawValue from, com.google.datastore.v1beta3.Value.Builder to) { to.mergeFrom(from.get()); } }; public static final class Builder - extends Value.BaseBuilder { + extends Value.BaseBuilder { private Builder() { super(ValueType.RAW_VALUE); @@ -65,7 +64,7 @@ private RawValue(Builder builder) { super(builder); } - RawValue(DatastoreV1.Value valuePb) { + RawValue(com.google.datastore.v1beta3.Value valuePb) { this(builder(valuePb)); } @@ -74,18 +73,14 @@ public Builder toBuilder() { return new Builder().mergeFrom(this); } - static RawValue of(DatastoreV1.Value valuePb) { + static RawValue of(com.google.datastore.v1beta3.Value valuePb) { return new RawValue(valuePb); } - static Builder builder(DatastoreV1.Value valuePb) { + static Builder builder(com.google.datastore.v1beta3.Value valuePb) { Builder builder = new Builder(); - if (valuePb.hasIndexed()) { - builder.indexed(valuePb.getIndexed()); - } - if (valuePb.hasMeaning()) { - builder.meaning(valuePb.getMeaning()); - } + builder.excludeFromIndexes(valuePb.getExcludeFromIndexes()); + builder.meaning(valuePb.getMeaning()); builder.set(valuePb); return builder; } diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ReadOption.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ReadOption.java new file mode 100644 index 000000000000..baadfe4f9bd2 --- /dev/null +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ReadOption.java @@ -0,0 +1,68 @@ +/* + * Copyright 2016 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.cloud.datastore; + +import com.google.common.collect.ImmutableMap; + +import java.io.Serializable; +import java.util.Map; + +/** + * Specifies options for read operations in Datastore, namely getting/fetching entities and running + * queries. + */ +public abstract class ReadOption implements Serializable { + + private static final long serialVersionUID = -4406964829189800528L; + + /** + * Specifies eventual consistency for reads from Datastore. Lookups and ancestor queries using + * this option permit Datastore to return stale results. + */ + public static final class EventualConsistency extends ReadOption { + + private static final long serialVersionUID = -6959530217724666172L; + + private final boolean eventualConsistency; + + private EventualConsistency(boolean eventualConsistency) { + this.eventualConsistency = eventualConsistency; + } + + public boolean isEventual() { + return eventualConsistency; + } + } + + private ReadOption() {} + + /** + * Returns a {@code ReadOption} that specifies eventual consistency, allowing Datastore to return + * stale results from gets, fetches, and ancestor queries. + */ + public static EventualConsistency eventualConsistency() { + return new EventualConsistency(true); + } + + static Map, ReadOption> asImmutableMap(ReadOption... options) { + ImmutableMap.Builder, ReadOption> builder = ImmutableMap.builder(); + for (ReadOption option : options) { + builder.put(option.getClass(), option); + } + return builder.build(); + } +} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Serializable.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Serializable.java similarity index 98% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Serializable.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Serializable.java index 89d19bcfd892..2e305935f32e 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Serializable.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Serializable.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import com.google.protobuf.GeneratedMessage; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StringValue.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StringValue.java similarity index 85% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StringValue.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StringValue.java index 95a31e714876..c47d2d9a61a4 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StringValue.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StringValue.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import static com.google.api.services.datastore.DatastoreV1.Value.STRING_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; +import static com.google.datastore.v1beta3.Value.STRING_VALUE_FIELD_NUMBER; public final class StringValue extends Value { @@ -40,12 +38,12 @@ public Builder newBuilder(String value) { } @Override - protected String getValue(DatastoreV1.Value from) { + protected String getValue(com.google.datastore.v1beta3.Value from) { return from.getStringValue(); } @Override - protected void setValue(StringValue from, DatastoreV1.Value.Builder to) { + protected void setValue(StringValue from, com.google.datastore.v1beta3.Value.Builder to) { to.setStringValue(from.get()); } }; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StructuredQuery.java similarity index 74% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StructuredQuery.java index 5892268f859c..442df20c8027 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/StructuredQuery.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/StructuredQuery.java @@ -14,18 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.datastore; - +package com.google.cloud.datastore; + +import static com.google.cloud.datastore.BlobValue.of; +import static com.google.cloud.datastore.BooleanValue.of; +import static com.google.cloud.datastore.DateTimeValue.of; +import static com.google.cloud.datastore.DoubleValue.of; +import static com.google.cloud.datastore.KeyValue.of; +import static com.google.cloud.datastore.LongValue.of; +import static com.google.cloud.datastore.StringValue.of; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.datastore.BlobValue.of; -import static com.google.gcloud.datastore.BooleanValue.of; -import static com.google.gcloud.datastore.DateTimeValue.of; -import static com.google.gcloud.datastore.DoubleValue.of; -import static com.google.gcloud.datastore.KeyValue.of; -import static com.google.gcloud.datastore.LongValue.of; -import static com.google.gcloud.datastore.StringValue.of; - -import com.google.api.services.datastore.DatastoreV1; + import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Preconditions; @@ -53,14 +52,14 @@ * Entity entity = results.next(); * ... * } - * } + * } * *

A simple key-only query of all entities for a specific kind *

 {@code
  * Query keyOnlyQuery =  Query.keyQueryBuilder().kind(KIND1).build();
  * QueryResults results = datastore.run(keyOnlyQuery);
  * ...
- * } 
+ * } * *

A less trivial example of a projection query that returns the first 10 results * of "age" and "name" properties (sorted and grouped by "age") with an age greater than 18 @@ -75,7 +74,7 @@ * .build(); * QueryResults results = datastore.run(query); * ... - * } + * } * * @param the type of the result values this query will produce * @see Datastore @@ -87,9 +86,9 @@ public abstract class StructuredQuery extends Query { static final String KEY_PROPERTY_NAME = "__key__"; private final transient String kind; - private final ImmutableList projection; + private final ImmutableList projection; private final transient Filter filter; - private final ImmutableList groupBy; + private final ImmutableList distinctOn; private final transient ImmutableList orderBy; private final transient Cursor startCursor; private final transient Cursor endCursor; @@ -103,13 +102,17 @@ public abstract static class Filter implements Serializable { Filter() { } - abstract DatastoreV1.Filter toPb(); + abstract com.google.datastore.v1beta3.Filter toPb(); - static Filter fromPb(DatastoreV1.Filter filterPb) { - if (filterPb.hasCompositeFilter()) { - return CompositeFilter.fromPb(filterPb.getCompositeFilter()); + static Filter fromPb(com.google.datastore.v1beta3.Filter filterPb) { + switch (filterPb.getFilterTypeCase()) { + case COMPOSITE_FILTER: + return CompositeFilter.fromPb(filterPb.getCompositeFilter()); + case PROPERTY_FILTER: + return PropertyFilter.fromPb(filterPb.getPropertyFilter()); + default: + throw new AssertionError("Unexpected enum value " + filterPb.getFilterTypeCase()); } - return PropertyFilter.fromPb(filterPb.getPropertyFilter()); } } @@ -125,11 +128,11 @@ public static final class CompositeFilter extends Filter { enum Operator { AND; - DatastoreV1.CompositeFilter.Operator toPb() { - return DatastoreV1.CompositeFilter.Operator.valueOf(name()); + com.google.datastore.v1beta3.CompositeFilter.Operator toPb() { + return com.google.datastore.v1beta3.CompositeFilter.Operator.valueOf(name()); } - static Operator fromPb(DatastoreV1.CompositeFilter.Operator operatorPb) { + static Operator fromPb(com.google.datastore.v1beta3.CompositeFilter.Operator operatorPb) { return valueOf(operatorPb.name()); } } @@ -172,11 +175,14 @@ public boolean equals(Object obj) { && filters.equals(other.filters); } - static CompositeFilter fromPb(DatastoreV1.CompositeFilter compositeFilterPb) { - Operator operator = Operator.fromPb(compositeFilterPb.getOperator()); + static CompositeFilter fromPb(com.google.datastore.v1beta3.CompositeFilter compositeFilterPb) { + Operator operator = Operator.fromPb(compositeFilterPb.getOp()); ImmutableList.Builder filters = ImmutableList.builder(); - for (DatastoreV1.Filter filterPb : compositeFilterPb.getFilterList()) { - filters.add(Filter.fromPb(filterPb)); + for (com.google.datastore.v1beta3.Filter filterPb : compositeFilterPb.getFiltersList()) { + Filter currFilter = Filter.fromPb(filterPb); + if (currFilter != null) { + filters.add(currFilter); + } } return new CompositeFilter(operator, filters.build()); } @@ -186,12 +192,14 @@ public static CompositeFilter and(Filter first, Filter... other) { } @Override - DatastoreV1.Filter toPb() { - DatastoreV1.Filter.Builder filterPb = DatastoreV1.Filter.newBuilder(); - DatastoreV1.CompositeFilter.Builder compositeFilterPb = filterPb.getCompositeFilterBuilder(); - compositeFilterPb.setOperator(operator.toPb()); + com.google.datastore.v1beta3.Filter toPb() { + com.google.datastore.v1beta3.Filter.Builder filterPb = + com.google.datastore.v1beta3.Filter.newBuilder(); + com.google.datastore.v1beta3.CompositeFilter.Builder compositeFilterPb = + filterPb.getCompositeFilterBuilder(); + compositeFilterPb.setOp(operator.toPb()); for (Filter filter : filters) { - compositeFilterPb.addFilter(filter.toPb()); + compositeFilterPb.addFilters(filter.toPb()); } return filterPb.build(); } @@ -216,11 +224,11 @@ enum Operator { EQUAL, HAS_ANCESTOR; - DatastoreV1.PropertyFilter.Operator toPb() { - return DatastoreV1.PropertyFilter.Operator.valueOf(name()); + com.google.datastore.v1beta3.PropertyFilter.Operator toPb() { + return com.google.datastore.v1beta3.PropertyFilter.Operator.valueOf(name()); } - static Operator fromPb(DatastoreV1.PropertyFilter.Operator operatorPb) { + static Operator fromPb(com.google.datastore.v1beta3.PropertyFilter.Operator operatorPb) { return valueOf(operatorPb.name()); } } @@ -231,9 +239,9 @@ private PropertyFilter(String property, Operator operator, Value value) { this.value = checkNotNull(value); } - static PropertyFilter fromPb(DatastoreV1.PropertyFilter propertyFilterPb) { + static PropertyFilter fromPb(com.google.datastore.v1beta3.PropertyFilter propertyFilterPb) { String property = propertyFilterPb.getProperty().getName(); - Operator operator = Operator.fromPb(propertyFilterPb.getOperator()); + Operator operator = Operator.fromPb(propertyFilterPb.getOp()); Value value = Value.fromPb(propertyFilterPb.getValue()); return new PropertyFilter(property, operator, value); } @@ -435,11 +443,13 @@ public static PropertyFilter isNull(String property) { } @Override - DatastoreV1.Filter toPb() { - DatastoreV1.Filter.Builder filterPb = DatastoreV1.Filter.newBuilder(); - DatastoreV1.PropertyFilter.Builder propertyFilterPb = filterPb.getPropertyFilterBuilder(); + com.google.datastore.v1beta3.Filter toPb() { + com.google.datastore.v1beta3.Filter.Builder filterPb = + com.google.datastore.v1beta3.Filter.newBuilder(); + com.google.datastore.v1beta3.PropertyFilter.Builder propertyFilterPb = + filterPb.getPropertyFilterBuilder(); propertyFilterPb.getPropertyBuilder().setName(property); - propertyFilterPb.setOperator(operator.toPb()); + propertyFilterPb.setOp(operator.toPb()); if (value != null) { propertyFilterPb.setValue(value.toPb()); } @@ -458,11 +468,11 @@ public enum Direction { ASCENDING, DESCENDING; - DatastoreV1.PropertyOrder.Direction toPb() { - return DatastoreV1.PropertyOrder.Direction.valueOf(name()); + com.google.datastore.v1beta3.PropertyOrder.Direction toPb() { + return com.google.datastore.v1beta3.PropertyOrder.Direction.valueOf(name()); } - static Direction fromPb(DatastoreV1.PropertyOrder.Direction directionPb) { + static Direction fromPb(com.google.datastore.v1beta3.PropertyOrder.Direction directionPb) { return valueOf(directionPb.name()); } } @@ -498,10 +508,11 @@ public Direction direction() { return direction; } - DatastoreV1.PropertyOrder toPb() { - return DatastoreV1.PropertyOrder.newBuilder() + com.google.datastore.v1beta3.PropertyOrder toPb() { + return com.google.datastore.v1beta3.PropertyOrder.newBuilder() .setDirection(direction.toPb()) - .setProperty(DatastoreV1.PropertyReference.newBuilder().setName(property).build()) + .setProperty(com.google.datastore.v1beta3.PropertyReference.newBuilder() + .setName(property).build()) .build(); } @@ -513,102 +524,13 @@ public static OrderBy desc(String property) { return new OrderBy(property, OrderBy.Direction.DESCENDING); } - static OrderBy fromPb(DatastoreV1.PropertyOrder propertyOrderPb) { + static OrderBy fromPb(com.google.datastore.v1beta3.PropertyOrder propertyOrderPb) { String property = propertyOrderPb.getProperty().getName(); Direction direction = Direction.fromPb(propertyOrderPb.getDirection()); return new OrderBy(property, direction); } } - /** - * A class representing a projection based on a property. - */ - public static final class Projection implements Serializable { - - private static final long serialVersionUID = 3083707957256279470L; - - private final Aggregate aggregate; - private final String property; - - public enum Aggregate { - - FIRST; - - DatastoreV1.PropertyExpression.AggregationFunction toPb() { - return DatastoreV1.PropertyExpression.AggregationFunction.valueOf(name()); - } - - static Aggregate fromPb(DatastoreV1.PropertyExpression.AggregationFunction aggregatePb) { - return valueOf(aggregatePb.name()); - } - } - - private Projection(Aggregate aggregate, String property) { - this.aggregate = aggregate; - this.property = property; - } - - @Override - public int hashCode() { - return Objects.hash(property, aggregate); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Projection)) { - return false; - } - Projection other = (Projection) obj; - return Objects.equals(property, other.property) - && Objects.equals(aggregate, other.aggregate); - } - - @Override - public String toString() { - ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); - toStringHelper.add("property", property); - if (aggregate != null) { - toStringHelper.add("aggregate", aggregate); - } - return toStringHelper.toString(); - } - - DatastoreV1.PropertyExpression toPb() { - DatastoreV1.PropertyExpression.Builder expressionPb = - DatastoreV1.PropertyExpression.newBuilder(); - if (aggregate != null) { - expressionPb.setAggregationFunction(aggregate.toPb()); - } - expressionPb.setProperty( - DatastoreV1.PropertyReference.newBuilder().setName(property).build()); - return expressionPb.build(); - } - - static Projection fromPb(DatastoreV1.PropertyExpression propertyExpressionPb) { - String property = propertyExpressionPb.getProperty().getName(); - Aggregate aggregate = null; - if (propertyExpressionPb.hasAggregationFunction()) { - aggregate = Aggregate.fromPb(propertyExpressionPb.getAggregationFunction()); - } - return new Projection(aggregate, property); - } - - public static Projection property(String property) { - return new Projection(null, property); - } - - public static Projection aggregate(Aggregate aggregate, String property) { - return new Projection(aggregate, property); - } - - public static Projection first(String property) { - return new Projection(Aggregate.FIRST, property); - } - } - /** * Interface for StructuredQuery builders. * @@ -655,9 +577,9 @@ abstract static class BuilderImpl> implements Bui private final ResultType resultType; private String namespace; private String kind; - private final List projection = new LinkedList<>(); + private final List projection = new LinkedList<>(); private Filter filter; - private final List groupBy = new LinkedList<>(); + private final List distinctOn = new LinkedList<>(); private final List orderBy = new LinkedList<>(); private Cursor startCursor; private Cursor endCursor; @@ -674,7 +596,7 @@ abstract static class BuilderImpl> implements Bui kind = query.kind; projection.addAll(query.projection); filter = query.filter; - groupBy.addAll(query.groupBy); + distinctOn.addAll(query.distinctOn); orderBy.addAll(query.orderBy); startCursor = query.startCursor; endCursor = query.endCursor; @@ -756,62 +678,65 @@ B clearProjection() { return self(); } - B projection(Projection projection, Projection... others) { + B projection(String projection, String... others) { clearProjection(); addProjection(projection, others); return self(); } - B addProjection(Projection projection, Projection... others) { + B addProjection(String projection, String... others) { this.projection.add(projection); Collections.addAll(this.projection, others); return self(); } - B clearGroupBy() { - groupBy.clear(); + B clearDistinctOn() { + distinctOn.clear(); return self(); } - B groupBy(String property, String... others) { - clearGroupBy(); - addGroupBy(property, others); + B distinctOn(String property, String... others) { + clearDistinctOn(); + addDistinctOn(property, others); return self(); } - B addGroupBy(String property, String... others) { - this.groupBy.add(property); - Collections.addAll(this.groupBy, others); + B addDistinctOn(String property, String... others) { + this.distinctOn.add(property); + Collections.addAll(this.distinctOn, others); return self(); } - B mergeFrom(DatastoreV1.Query queryPb) { + B mergeFrom(com.google.datastore.v1beta3.Query queryPb) { if (queryPb.getKindCount() > 0) { kind(queryPb.getKind(0).getName()); } - if (queryPb.hasStartCursor()) { + if (!queryPb.getStartCursor().isEmpty()) { startCursor(new Cursor(queryPb.getStartCursor())); } - if (queryPb.hasEndCursor()) { + if (!queryPb.getEndCursor().isEmpty()) { endCursor(new Cursor(queryPb.getEndCursor())); } - if (queryPb.hasOffset()) { - offset(queryPb.getOffset()); - } + offset(queryPb.getOffset()); if (queryPb.hasLimit()) { - limit(queryPb.getLimit()); + limit(queryPb.getLimit().getValue()); } if (queryPb.hasFilter()) { - filter(Filter.fromPb(queryPb.getFilter())); + Filter currFilter = Filter.fromPb(queryPb.getFilter()); + if (currFilter != null) { + filter(currFilter); + } } - for (DatastoreV1.PropertyOrder orderByPb : queryPb.getOrderList()) { + for (com.google.datastore.v1beta3.PropertyOrder orderByPb : queryPb.getOrderList()) { addOrderBy(OrderBy.fromPb(orderByPb)); } - for (DatastoreV1.PropertyExpression projectionPb : queryPb.getProjectionList()) { - addProjection(Projection.fromPb(projectionPb)); + for (com.google.datastore.v1beta3.Projection projectionPb + : queryPb.getProjectionList()) { + addProjection(projectionPb.getProperty().getName()); } - for (DatastoreV1.PropertyReference groupByPb : queryPb.getGroupByList()) { - addGroupBy(groupByPb.getName()); + for (com.google.datastore.v1beta3.PropertyReference distinctOnPb : + queryPb.getDistinctOnList()) { + addDistinctOn(distinctOnPb.getName()); } return self(); } @@ -822,7 +747,7 @@ B mergeFrom(DatastoreV1.Query queryPb) { kind = builder.kind; projection = ImmutableList.copyOf(builder.projection); filter = builder.filter; - groupBy = ImmutableList.copyOf(builder.groupBy); + distinctOn = ImmutableList.copyOf(builder.distinctOn); orderBy = ImmutableList.copyOf(builder.orderBy); startCursor = builder.startCursor; endCursor = builder.endCursor; @@ -833,7 +758,7 @@ B mergeFrom(DatastoreV1.Query queryPb) { @Override public int hashCode() { return Objects.hash(namespace(), kind, startCursor, endCursor, offset, limit, filter, orderBy, - projection(), groupBy()); +distinctOn()); } @Override @@ -854,7 +779,7 @@ public boolean equals(Object obj) { && Objects.equals(filter, other.filter) && Objects.equals(orderBy, other.orderBy) && Objects.equals(projection, other.projection) - && Objects.equals(groupBy, other.groupBy); + && Objects.equals(distinctOn, other.distinctOn); } @@ -863,10 +788,10 @@ public String kind() { } boolean keyOnly() { - return projection.size() == 1 && KEY_PROPERTY_NAME.equals(projection.get(0).property); + return projection.size() == 1 && KEY_PROPERTY_NAME.equals(projection.get(0)); } - public List projection() { + public List projection() { return projection; } @@ -874,8 +799,8 @@ public Filter filter() { return filter; } - public List groupBy() { - return groupBy; + public List distinctOn() { + return distinctOn; } public ImmutableList orderBy() { @@ -901,28 +826,29 @@ public Integer limit() { public abstract Builder toBuilder(); @Override - void populatePb(DatastoreV1.RunQueryRequest.Builder requestPb) { + void populatePb(com.google.datastore.v1beta3.RunQueryRequest.Builder requestPb) { requestPb.setQuery(toPb()); } @Override - StructuredQuery nextQuery(DatastoreV1.QueryResultBatch responsePb) { + StructuredQuery nextQuery(com.google.datastore.v1beta3.RunQueryResponse responsePb) { Builder builder = toBuilder(); - builder.startCursor(new Cursor(responsePb.getEndCursor())); - if (offset > 0 && responsePb.getSkippedResults() < offset) { - builder.offset(offset - responsePb.getSkippedResults()); + builder.startCursor(new Cursor(responsePb.getBatch().getEndCursor())); + if (offset > 0 && responsePb.getBatch().getSkippedResults() < offset) { + builder.offset(offset - responsePb.getBatch().getSkippedResults()); } else { builder.offset(0); if (limit != null) { - builder.limit(limit - responsePb.getEntityResultCount()); + builder.limit(limit - responsePb.getBatch().getEntityResultsCount()); } } return builder.build(); } @Override - DatastoreV1.Query toPb() { - DatastoreV1.Query.Builder queryPb = DatastoreV1.Query.newBuilder(); + com.google.datastore.v1beta3.Query toPb() { + com.google.datastore.v1beta3.Query.Builder queryPb = + com.google.datastore.v1beta3.Query.newBuilder(); if (kind != null) { queryPb.addKindBuilder().setName(kind); } @@ -936,7 +862,7 @@ DatastoreV1.Query toPb() { queryPb.setOffset(offset); } if (limit != null) { - queryPb.setLimit(limit); + queryPb.setLimit(com.google.protobuf.Int32Value.newBuilder().setValue(limit.intValue())); } if (filter != null) { queryPb.setFilter(filter.toPb()); @@ -944,11 +870,16 @@ DatastoreV1.Query toPb() { for (OrderBy value : orderBy) { queryPb.addOrder(value.toPb()); } - for (String value : groupBy) { - queryPb.addGroupBy(DatastoreV1.PropertyReference.newBuilder().setName(value).build()); + for (String value : distinctOn) { + queryPb.addDistinctOn(com.google.datastore.v1beta3.PropertyReference.newBuilder() + .setName(value).build()); } - for (Projection value : projection) { - queryPb.addProjection(value.toPb()); + for (String value : projection) { + com.google.datastore.v1beta3.Projection.Builder expressionPb = + com.google.datastore.v1beta3.Projection.newBuilder(); + expressionPb.setProperty( + com.google.datastore.v1beta3.PropertyReference.newBuilder().setName(value).build()); + queryPb.addProjection(expressionPb.build()); } return queryPb.build(); } @@ -956,11 +887,12 @@ DatastoreV1.Query toPb() { @Override Object fromPb(ResultType resultType, String namespace, byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(resultType, namespace, DatastoreV1.Query.parseFrom(bytesPb)); + return fromPb(resultType, namespace, com.google.datastore.v1beta3.Query.parseFrom(bytesPb)); } - static StructuredQuery fromPb(ResultType resultType, String namespace, - DatastoreV1.Query queryPb) { + @SuppressWarnings("unchecked") + static StructuredQuery fromPb(ResultType resultType, String namespace, + com.google.datastore.v1beta3.Query queryPb) { BuilderImpl builder; if (resultType.equals(ResultType.ENTITY)) { builder = new EntityQuery.Builder(); @@ -969,6 +901,6 @@ static StructuredQuery fromPb(ResultType resultType, String namespace, } else { builder = new ProjectionEntityQuery.Builder(); } - return builder.namespace(namespace).mergeFrom(queryPb).build(); + return (StructuredQuery) builder.namespace(namespace).mergeFrom(queryPb).build(); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Transaction.java similarity index 99% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Transaction.java index 78ee217f31e7..9ecf2c8caeb6 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Transaction.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Transaction.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import java.util.Iterator; import java.util.List; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/TransactionImpl.java similarity index 52% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/TransactionImpl.java index ae677aa005d6..9016d561905a 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionImpl.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/TransactionImpl.java @@ -14,58 +14,47 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.Function; -import com.google.common.collect.Lists; -import com.google.gcloud.datastore.TransactionOption.ForceWrites; -import com.google.gcloud.datastore.TransactionOption.IsolationLevel; import com.google.protobuf.ByteString; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; final class TransactionImpl extends BaseDatastoreBatchWriter implements Transaction { private final DatastoreImpl datastore; private final ByteString transaction; - private final boolean force; private boolean rolledback; static class ResponseImpl implements Transaction.Response { - private final DatastoreV1.CommitResponse response; + private final com.google.datastore.v1beta3.CommitResponse response; + private final int numAutoAllocatedIds; - ResponseImpl(DatastoreV1.CommitResponse response) { + ResponseImpl(com.google.datastore.v1beta3.CommitResponse response, int numAutoAllocatedIds) { this.response = response; + this.numAutoAllocatedIds = numAutoAllocatedIds; } @Override public List generatedKeys() { - return Lists.transform(response.getMutationResult().getInsertAutoIdKeyList(), - new Function() { - @Override public Key apply(DatastoreV1.Key keyPb) { - return Key.fromPb(keyPb); - } - }); + Iterator results = + response.getMutationResultsList().iterator(); + List generated = new ArrayList<>(numAutoAllocatedIds); + for (int i = 0; i < numAutoAllocatedIds; i++) { + generated.add(Key.fromPb(results.next().getKey())); + } + return generated; } } - TransactionImpl(DatastoreImpl datastore, TransactionOption... options) { + TransactionImpl(DatastoreImpl datastore) { super("transaction"); this.datastore = datastore; - DatastoreV1.BeginTransactionRequest.Builder requestPb = - DatastoreV1.BeginTransactionRequest.newBuilder(); - Map, TransactionOption> optionsMap = - TransactionOption.asImmutableMap(options); - IsolationLevel isolationLevel = (IsolationLevel) optionsMap.get(IsolationLevel.class); - if (isolationLevel != null) { - requestPb.setIsolationLevel(isolationLevel.level().toPb()); - } - ForceWrites forceWrites = (ForceWrites) optionsMap.get(TransactionOption.ForceWrites.class); - force = forceWrites != null && forceWrites.force(); + com.google.datastore.v1beta3.BeginTransactionRequest.Builder requestPb = + com.google.datastore.v1beta3.BeginTransactionRequest.newBuilder(); transaction = datastore.requestTransactionId(requestPb); } @@ -77,7 +66,8 @@ public Entity get(Key key) { @Override public Iterator get(Key... keys) { validateActive(); - DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); + com.google.datastore.v1beta3.ReadOptions.Builder readOptionsPb = + com.google.datastore.v1beta3.ReadOptions.newBuilder(); readOptionsPb.setTransaction(transaction); return datastore.get(readOptionsPb.build(), keys); } @@ -91,7 +81,8 @@ public List fetch(Key... keys) { @Override public QueryResults run(Query query) { validateActive(); - DatastoreV1.ReadOptions.Builder readOptionsPb = DatastoreV1.ReadOptions.newBuilder(); + com.google.datastore.v1beta3.ReadOptions.Builder readOptionsPb = + com.google.datastore.v1beta3.ReadOptions.newBuilder(); readOptionsPb.setTransaction(transaction); return datastore.run(readOptionsPb.build(), query); } @@ -99,17 +90,15 @@ public QueryResults run(Query query) { @Override public Transaction.Response commit() { validateActive(); - DatastoreV1.Mutation.Builder mutationPb = toMutationPb(); - if (force) { - mutationPb.setForce(force); - } - DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); - requestPb.setMode(DatastoreV1.CommitRequest.Mode.TRANSACTIONAL); + List mutationsPb = toMutationPbList(); + com.google.datastore.v1beta3.CommitRequest.Builder requestPb = + com.google.datastore.v1beta3.CommitRequest.newBuilder(); + requestPb.setMode(com.google.datastore.v1beta3.CommitRequest.Mode.TRANSACTIONAL); requestPb.setTransaction(transaction); - requestPb.setMutation(mutationPb); - DatastoreV1.CommitResponse responsePb = datastore.commit(requestPb.build()); + requestPb.addAllMutations(mutationsPb); + com.google.datastore.v1beta3.CommitResponse responsePb = datastore.commit(requestPb.build()); deactivate(); - return new ResponseImpl(responsePb); + return new ResponseImpl(responsePb, toAddAutoId().size()); } @Override diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Validator.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Validator.java similarity index 80% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Validator.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Validator.java index 09a3fa7defcc..31857af73ddc 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Validator.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Validator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkArgument; @@ -45,13 +45,11 @@ static String validateDatabase(String projectId) { } static String validateNamespace(String namespace) { - if (namespace != null) { - checkArgument(!namespace.isEmpty(), "namespace must not be an empty string"); - checkArgument(namespace.length() <= MAX_NAMESPACE_LENGTH, - "namespace must not contain more than 100 characters"); - checkArgument(NAMESPACE_PATTERN.matcher(namespace).matches(), - "namespace must the following pattern: " + NAMESPACE_PATTERN.pattern()); - } + checkArgument(namespace != null, "Namespace cannot be null. Leave unset for default."); + checkArgument(namespace.length() <= MAX_NAMESPACE_LENGTH, + "namespace must not contain more than 100 characters"); + checkArgument(NAMESPACE_PATTERN.matcher(namespace).matches(), + "namespace must the following pattern: " + NAMESPACE_PATTERN.pattern()); return namespace; } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Value.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Value.java similarity index 63% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Value.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Value.java index 4d8aaec086f1..c2c503c8f9db 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/Value.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/Value.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.services.datastore.DatastoreV1; -import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.datastore.v1beta3.Value.ValueTypeCase; import com.google.protobuf.InvalidProtocolBufferException; -import java.util.Map.Entry; import java.util.Objects; /** @@ -33,14 +31,13 @@ * * @param the type of the content for this value */ -public abstract class Value extends Serializable { +public abstract class Value extends Serializable { private static final long serialVersionUID = -1899638277588872742L; - private final transient ValueType valueType; - private final transient Boolean indexed; - private final transient Integer meaning; + private final transient boolean excludeFromIndexes; + private final transient int meaning; private final transient V value; interface BuilderFactory, B extends ValueBuilder> @@ -55,43 +52,35 @@ abstract static class BaseMarshaller, B extends ValueBuild @SuppressWarnings("deprecation") @Override - public final B fromProto(DatastoreV1.Value proto) { + public final B fromProto(com.google.datastore.v1beta3.Value proto) { B builder = newBuilder(getValue(proto)); - if (proto.hasIndexed()) { - builder.indexed(proto.getIndexed()); - } - if (proto.hasMeaning()) { - builder.meaning(proto.getMeaning()); - } + builder.excludeFromIndexes(proto.getExcludeFromIndexes()); + builder.meaning(proto.getMeaning()); return builder; } @SuppressWarnings("deprecation") @Override - public final DatastoreV1.Value toProto(P value) { - DatastoreV1.Value.Builder builder = DatastoreV1.Value.newBuilder(); - if (value.hasIndexed()) { - builder.setIndexed(value.indexed()); - } - if (value.hasMeaning()) { - builder.setMeaning(value.meaning()); - } + public final com.google.datastore.v1beta3.Value toProto(P value) { + com.google.datastore.v1beta3.Value.Builder builder = + com.google.datastore.v1beta3.Value.newBuilder(); + builder.setExcludeFromIndexes(value.excludeFromIndexes()); + builder.setMeaning(value.meaning()); setValue(value, builder); return builder.build(); } - protected abstract V getValue(DatastoreV1.Value from); + protected abstract V getValue(com.google.datastore.v1beta3.Value from); - protected abstract void setValue(P from, DatastoreV1.Value.Builder to); + protected abstract void setValue(P from, com.google.datastore.v1beta3.Value.Builder to); } - @SuppressWarnings("deprecation") abstract static class BaseBuilder, B extends BaseBuilder> implements ValueBuilder { private final ValueType valueType; - private Boolean indexed; - private Integer meaning; + private boolean excludeFromIndexes; + private int meaning; private V value; BaseBuilder(ValueType valueType) { @@ -105,30 +94,32 @@ public ValueType getValueType() { @Override public B mergeFrom(P other) { - indexed = other.indexed(); + excludeFromIndexes = other.excludeFromIndexes(); meaning = other.meaning(); set(other.get()); return self(); } @Override - public Boolean getIndexed() { - return indexed; + public boolean getExcludeFromIndexes() { + return excludeFromIndexes; } @Override - public B indexed(boolean indexed) { - this.indexed = indexed; + public B excludeFromIndexes(boolean excludeFromIndexes) { + this.excludeFromIndexes = excludeFromIndexes; return self(); } + @Deprecated @Override - public Integer getMeaning() { + public int getMeaning() { return meaning; } + @Deprecated @Override - public B meaning(Integer meaning) { + public B meaning(int meaning) { this.meaning = meaning; return self(); } @@ -153,9 +144,10 @@ private B self() { public abstract P build(); } + @SuppressWarnings("deprecation")

, B extends BaseBuilder> Value(ValueBuilder builder) { valueType = builder.getValueType(); - indexed = builder.getIndexed(); + excludeFromIndexes = builder.getExcludeFromIndexes(); meaning = builder.getMeaning(); value = builder.get(); } @@ -164,21 +156,12 @@ public final ValueType type() { return valueType; } - public final boolean hasIndexed() { - return indexed != null; - } - - public final Boolean indexed() { - return indexed; + public final boolean excludeFromIndexes() { + return excludeFromIndexes; } @Deprecated - public final boolean hasMeaning() { - return meaning != null; - } - - @Deprecated - public final Integer meaning() { + final int meaning() { return meaning; } @@ -190,7 +173,7 @@ public final V get() { @Override public int hashCode() { - return Objects.hash(valueType, indexed, meaning, value); + return Objects.hash(valueType, excludeFromIndexes, meaning, value); } @Override @@ -204,34 +187,26 @@ public boolean equals(Object obj) { } Value other = (Value) obj; return Objects.equals(valueType, other.valueType) - && Objects.equals(indexed, other.indexed) + && Objects.equals(excludeFromIndexes, other.excludeFromIndexes) && Objects.equals(meaning, other.meaning) && Objects.equals(value, other.value); } @Override @SuppressWarnings("unchecked") - DatastoreV1.Value toPb() { + com.google.datastore.v1beta3.Value toPb() { return type().getMarshaller().toProto(this); } - static Value fromPb(DatastoreV1.Value proto) { - for (Entry entry : proto.getAllFields().entrySet()) { - FieldDescriptor descriptor = entry.getKey(); - if (descriptor.getName().endsWith("_value")) { - ValueType valueType = ValueType.getByDescriptorId(descriptor.getNumber()); - if (valueType == null) { - // unsupported type - return RawValue.MARSHALLER.fromProto(proto).build(); - } - return valueType.getMarshaller().fromProto(proto).build(); - } - } - return NullValue.MARSHALLER.fromProto(proto).build(); + static Value fromPb(com.google.datastore.v1beta3.Value proto) { + ValueTypeCase descriptorId = proto.getValueTypeCase(); + ValueType valueType = ValueType.getByDescriptorId(descriptorId.getNumber()); + return valueType == null ? RawValue.MARSHALLER.fromProto(proto).build() + : valueType.getMarshaller().fromProto(proto).build(); } @Override Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException { - return fromPb(DatastoreV1.Value.parseFrom(bytesPb)); + return fromPb(com.google.datastore.v1beta3.Value.parseFrom(bytesPb)); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueBuilder.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueBuilder.java similarity index 73% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueBuilder.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueBuilder.java index a867ef25b321..0328d4a0637b 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueBuilder.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueBuilder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; /** * A common interface for Value builders. @@ -29,14 +29,21 @@ public interface ValueBuilder, B extends ValueBuilder, B extends ValueBuilder> extends java.io.Serializable { - B fromProto(DatastoreV1.Value proto); + B fromProto(com.google.datastore.v1beta3.Value proto); - DatastoreV1.Value toProto(P value); + com.google.datastore.v1beta3.Value toProto(P value); int getProtoFieldId(); } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueType.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueType.java similarity index 94% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueType.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueType.java index b09583103a59..2b023654742a 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ValueType.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/ValueType.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import com.google.common.collect.ImmutableMap; @@ -78,8 +78,12 @@ public enum ValueType { /** * Represents a raw/unparsed value. */ - RAW_VALUE(RawValue.MARSHALLER); + RAW_VALUE(RawValue.MARSHALLER), + /** + * Represents a {@link LatLng} value + */ + LAT_LNG(LatLngValue.MARSHALLER); private static final ImmutableMap DESCRIPTOR_TO_TYPE_MAP; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/package-info.java similarity index 90% rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/package-info.java index fbf468d6458d..f51c9545ecd2 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/package-info.java +++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/package-info.java @@ -19,7 +19,7 @@ * *

Here's a simple usage example for using gcloud-java from App/Compute Engine. This example * shows how to create a Datastore entity. For the complete source code see - * + * * CreateEntity.java. *

 {@code
  * Datastore datastore = DatastoreOptions.defaultInstance().service();
@@ -35,7 +35,7 @@
  * 

* This second example shows how to get and update a Datastore entity if it exists. For the complete * source code see - * + * * UpdateEntity.java. *

 {@code
  * Datastore datastore = DatastoreOptions.defaultInstance().service();
@@ -54,6 +54,7 @@
  * project ID and
  * provide
  * credentials.
+ *
  * @see Google Cloud Datastore
  */
-package com.google.gcloud.datastore;
+package com.google.cloud.datastore;
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpc.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpc.java
similarity index 63%
rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpc.java
rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpc.java
index 002078550d1f..d7d8aae673ba 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpc.java
+++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpc.java
@@ -14,21 +14,21 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.datastore.spi;
+package com.google.cloud.datastore.spi;
 
-import com.google.api.services.datastore.DatastoreV1.AllocateIdsRequest;
-import com.google.api.services.datastore.DatastoreV1.AllocateIdsResponse;
-import com.google.api.services.datastore.DatastoreV1.BeginTransactionRequest;
-import com.google.api.services.datastore.DatastoreV1.BeginTransactionResponse;
-import com.google.api.services.datastore.DatastoreV1.CommitRequest;
-import com.google.api.services.datastore.DatastoreV1.CommitResponse;
-import com.google.api.services.datastore.DatastoreV1.LookupRequest;
-import com.google.api.services.datastore.DatastoreV1.LookupResponse;
-import com.google.api.services.datastore.DatastoreV1.RollbackRequest;
-import com.google.api.services.datastore.DatastoreV1.RollbackResponse;
-import com.google.api.services.datastore.DatastoreV1.RunQueryRequest;
-import com.google.api.services.datastore.DatastoreV1.RunQueryResponse;
-import com.google.gcloud.datastore.DatastoreException;
+import com.google.cloud.datastore.DatastoreException;
+import com.google.datastore.v1beta3.AllocateIdsRequest;
+import com.google.datastore.v1beta3.AllocateIdsResponse;
+import com.google.datastore.v1beta3.BeginTransactionRequest;
+import com.google.datastore.v1beta3.BeginTransactionResponse;
+import com.google.datastore.v1beta3.CommitRequest;
+import com.google.datastore.v1beta3.CommitResponse;
+import com.google.datastore.v1beta3.LookupRequest;
+import com.google.datastore.v1beta3.LookupResponse;
+import com.google.datastore.v1beta3.RollbackRequest;
+import com.google.datastore.v1beta3.RollbackResponse;
+import com.google.datastore.v1beta3.RunQueryRequest;
+import com.google.datastore.v1beta3.RunQueryResponse;
 
 /**
  * Provides access to the remote Datastore service.
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpcFactory.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpcFactory.java
similarity index 85%
rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpcFactory.java
rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpcFactory.java
index 0979b2203037..c16a7ddb0fea 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DatastoreRpcFactory.java
+++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DatastoreRpcFactory.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.datastore.spi;
+package com.google.cloud.datastore.spi;
 
-import com.google.gcloud.datastore.DatastoreOptions;
-import com.google.gcloud.spi.ServiceRpcFactory;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.cloud.spi.ServiceRpcFactory;
 
 /**
  * An interface for Datastore RPC factory.
diff --git a/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DefaultDatastoreRpc.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DefaultDatastoreRpc.java
new file mode 100644
index 000000000000..255da4c6e229
--- /dev/null
+++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/spi/DefaultDatastoreRpc.java
@@ -0,0 +1,160 @@
+/*
+ * 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.cloud.datastore.spi;
+
+import com.google.cloud.datastore.DatastoreException;
+import com.google.cloud.datastore.DatastoreOptions;
+import com.google.datastore.v1beta3.AllocateIdsRequest;
+import com.google.datastore.v1beta3.AllocateIdsResponse;
+import com.google.datastore.v1beta3.BeginTransactionRequest;
+import com.google.datastore.v1beta3.BeginTransactionResponse;
+import com.google.datastore.v1beta3.CommitRequest;
+import com.google.datastore.v1beta3.CommitResponse;
+import com.google.datastore.v1beta3.LookupRequest;
+import com.google.datastore.v1beta3.LookupResponse;
+import com.google.datastore.v1beta3.RollbackRequest;
+import com.google.datastore.v1beta3.RollbackResponse;
+import com.google.datastore.v1beta3.RunQueryRequest;
+import com.google.datastore.v1beta3.RunQueryResponse;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+
+public class DefaultDatastoreRpc implements DatastoreRpc {
+
+  private final com.google.datastore.v1beta3.client.Datastore client;
+
+  public DefaultDatastoreRpc(DatastoreOptions options) {
+    com.google.datastore.v1beta3.client.DatastoreOptions.Builder clientBuilder =
+        new com.google.datastore.v1beta3.client.DatastoreOptions.Builder()
+            .projectId(options.projectId())
+            .initializer(options.httpRequestInitializer());
+    String normalizedHost = options.host() != null ? options.host().toLowerCase() : "";
+    if (isLocalHost(normalizedHost)) {
+      clientBuilder = clientBuilder.localHost(removeScheme(normalizedHost));
+    } else if (!removeScheme(com.google.datastore.v1beta3.client.DatastoreFactory.DEFAULT_HOST)
+                    .equals(removeScheme(normalizedHost))
+        && !normalizedHost.isEmpty()) {
+      String fullURL = normalizedHost;
+      if (fullURL.charAt(fullURL.length() - 1) != '/') {
+        fullURL = fullURL + '/';
+      }
+      fullURL = fullURL
+          + com.google.datastore.v1beta3.client.DatastoreFactory.VERSION
+          + "/projects/"
+          + options.projectId();
+      clientBuilder = clientBuilder.projectId(null).projectEndpoint(fullURL);
+    }
+    client = com.google.datastore.v1beta3.client.DatastoreFactory.get()
+        .create(clientBuilder.build());
+  }
+
+  private static boolean isLocalHost(String host) {
+    if (!host.isEmpty()) {
+      try {
+        String normalizedHost = "http://" + removeScheme(host);
+        InetAddress hostAddr = InetAddress.getByName(new URL(normalizedHost).getHost());
+        return hostAddr.isAnyLocalAddress() || hostAddr.isLoopbackAddress();
+      } catch (Exception e) {
+        // ignore
+      }
+    }
+    return false;
+  }
+
+  private static String removeScheme(String url) {
+    if (url != null) {
+      if (url.startsWith("https://")) {
+        return url.substring("https://".length());
+      } else if (url.startsWith("http://")) {
+        return url.substring("http://".length());
+      }
+    }
+    return url;
+  }
+
+  private static DatastoreException translate(
+      com.google.datastore.v1beta3.client.DatastoreException exception) {
+    String reason = "";
+    if (exception.getCode() != null) {
+      reason = exception.getCode().name();
+    }
+    if (reason.isEmpty()) {
+      if (exception.getCause() instanceof IOException) {
+        return new DatastoreException((IOException) exception.getCause());
+      }
+    }
+    return new DatastoreException(
+        exception.getCode().ordinal(), exception.getMessage(), reason, exception);
+  }
+
+  @Override
+  public AllocateIdsResponse allocateIds(AllocateIdsRequest request) {
+    try {
+      return client.allocateIds(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+
+      throw translate(ex);
+    }
+  }
+
+  @Override
+  public BeginTransactionResponse beginTransaction(BeginTransactionRequest request) {
+    try {
+      return client.beginTransaction(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+      throw translate(ex);
+    }
+  }
+
+  @Override
+  public CommitResponse commit(CommitRequest request) {
+    try {
+      return client.commit(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+      throw translate(ex);
+    }
+  }
+
+  @Override
+  public LookupResponse lookup(LookupRequest request) {
+    try {
+      return client.lookup(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+      throw translate(ex);
+    }
+  }
+
+  @Override
+  public RollbackResponse rollback(RollbackRequest request) {
+    try {
+      return client.rollback(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+      throw translate(ex);
+    }
+  }
+
+  @Override
+  public RunQueryResponse runQuery(RunQueryRequest request) {
+    try {
+      return client.runQuery(request);
+    } catch (com.google.datastore.v1beta3.client.DatastoreException ex) {
+      throw translate(ex);
+    }
+  }
+}
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/LocalGcdHelper.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/LocalDatastoreHelper.java
similarity index 80%
rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/LocalGcdHelper.java
rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/LocalDatastoreHelper.java
index fdb6774f810f..4da7e0d27e75 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/LocalGcdHelper.java
+++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/LocalDatastoreHelper.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.google.gcloud.datastore.testing;
+package com.google.cloud.datastore.testing;
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
-import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.cloud.AuthCredentials;
+import com.google.cloud.RetryParams;
+import com.google.cloud.datastore.DatastoreOptions;
 import com.google.common.base.Strings;
 
 import java.io.BufferedInputStream;
@@ -28,8 +30,6 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -52,10 +52,9 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
+import java.util.UUID;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
@@ -65,8 +64,19 @@
 /**
  * Utility to start and stop local Google Cloud Datastore process.
  */
-public class LocalGcdHelper {
-  private static final Logger log = Logger.getLogger(LocalGcdHelper.class.getName());
+public class LocalDatastoreHelper {
+  private static final Logger log = Logger.getLogger(LocalDatastoreHelper.class.getName());
+  private static final String GCD_VERSION = "v1beta3";
+  private static final String GCD_BUILD = "1.0.1";
+  private static final double DEFAULT_CONSISTENCY = 0.9;
+  private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD;
+  private static final String GCD_FILENAME = GCD_BASENAME + ".zip";
+  private static final String MD5_CHECKSUM = "df876ba8f054d69acff30ec9540ec386";
+  private static final URL GCD_URL;
+  private static final String GCLOUD = "gcloud";
+  private static final Path INSTALLED_GCD_PATH;
+  private static final String GCD_VERSION_PREFIX = "gcd-emulator ";
+  private static final String PROJECT_ID_PREFIX = "test-project-";
 
   private final String projectId;
   private Path gcdPath;
@@ -74,19 +84,7 @@ public class LocalGcdHelper {
   private ProcessStreamReader processReader;
   private ProcessErrorStreamReader processErrorReader;
   private final int port;
-
-  public static final String DEFAULT_PROJECT_ID = "projectid1";
-  public static final int DEFAULT_PORT = 8080;
-  private static final String GCD_VERSION = "v1beta2";
-  private static final String GCD_BUILD = "rev1-2.1.2b";
-  private static final String GCD_BASENAME = "gcd-" + GCD_VERSION + "-" + GCD_BUILD;
-  private static final String GCD_FILENAME = GCD_BASENAME + ".zip";
-  private static final String MD5_CHECKSUM = "d84384cdfa8658e1204f4f8be51300e8";
-  private static final URL GCD_URL;
-  private static final String GCLOUD = "gcloud";
-  private static final Path INSTALLED_GCD_PATH;
-  private static final String GCD_VERSION_PREFIX = "gcd-emulator ";
-  private static final double DEFAULT_CONSISTENCY = 0.9;
+  private final double consistency;
 
   static {
     INSTALLED_GCD_PATH = installedGcdPath();
@@ -101,14 +99,6 @@ public class LocalGcdHelper {
     }
   }
 
-  public static int findAvailablePort(int defaultPort) {
-    try (ServerSocket tempSocket = new ServerSocket(0)) {
-      return tempSocket.getLocalPort();
-    } catch (IOException e) {
-      return defaultPort;
-    }
-  }
-
   private static Path installedGcdPath() {
     String gcloudExecutableName;
     if (isWindows()) {
@@ -388,44 +378,6 @@ public static CommandWrapper create() {
     }
   }
 
-  public LocalGcdHelper(String projectId, int port) {
-    this.projectId = projectId;
-    this.port = port;
-  }
-
-  /**
-   * Starts the local datastore for the specific project.
-   *
-   * This will unzip the gcd tool, create the project and start it.
-   * All content is written to a temporary directory that will be deleted when
-   * {@link #stop()} is called or when the program terminates) to make sure that no left-over
-   * data from prior runs is used.
-   *
-   * @param consistency the fraction of job application attempts that will succeed, with 0.0
-   *     resulting in no attempts succeeding, and 1.0 resulting in all attempts succeeding. Defaults
-   *     to 0.9. Note that setting this to 1.0 may mask incorrect assumptions about the consistency
-   *     of non-ancestor queries; non-ancestor queries are eventually consistent.
-   */
-  public void start(double consistency) throws IOException, InterruptedException {
-    // send a quick request in case we have a hanging process from a previous run
-    checkArgument(consistency >= 0.0 && consistency <= 1.0, "Consistency must be between 0 and 1");
-    sendQuitRequest(port);
-    // Each run is associated with its own folder that is deleted once test completes.
-    gcdPath = Files.createTempDirectory("gcd");
-    File gcdFolder = gcdPath.toFile();
-    gcdFolder.deleteOnExit();
-
-    Path gcdExecutablePath;
-    // If cloud is available we use it, otherwise we download and start gcd
-    if (INSTALLED_GCD_PATH == null) {
-      downloadGcd();
-      gcdExecutablePath = gcdPath.resolve(GCD_BASENAME);
-    } else {
-      gcdExecutablePath = INSTALLED_GCD_PATH;
-    }
-    startGcd(gcdExecutablePath, consistency);
-  }
-
   private void downloadGcd() throws IOException {
     // check if we already have a local copy of the gcd utility and download it if not.
     File gcdZipFile = new File(System.getProperty("java.io.tmpdir"), GCD_FILENAME);
@@ -589,80 +541,96 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
     });
   }
 
-  public static LocalGcdHelper start(String projectId, int port, double consistency)
-      throws IOException, InterruptedException {
-    LocalGcdHelper helper = new LocalGcdHelper(projectId, port);
-    helper.start(consistency);
-    return helper;
+  private LocalDatastoreHelper(double consistency) {
+    checkArgument(consistency >= 0.0 && consistency <= 1.0, "Consistency must be between 0 and 1");
+    projectId = PROJECT_ID_PREFIX + UUID.randomUUID().toString();
+    this.consistency = consistency;
+    this.port = findAvailablePort();
   }
 
-  public static void main(String... args) throws IOException, InterruptedException {
-    Map parsedArgs = parseArgs(args);
-    String action = parsedArgs.get("action");
-    int port =
-        (parsedArgs.get("port") == null) ? DEFAULT_PORT : Integer.parseInt(parsedArgs.get("port"));
-    switch (action) {
-      case "START":
-        if (!isActive(DEFAULT_PROJECT_ID, port)) {
-          double consistency = parsedArgs.get("consistency") == null
-              ? DEFAULT_CONSISTENCY : Double.parseDouble(parsedArgs.get("consistency"));
-          LocalGcdHelper helper = start(DEFAULT_PROJECT_ID, port, consistency);
-          try (FileWriter writer = new FileWriter(".local_gcd_helper")) {
-            writer.write(helper.gcdPath.toAbsolutePath().toString() + System.lineSeparator());
-            writer.write(Integer.toString(port));
-          }
-        }
-        return;
-      case "STOP":
-        File file = new File(".local_gcd_helper");
-        String path = null;
-        boolean fileExists = file.exists();
-        if (fileExists) {
-          try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
-            path = reader.readLine();
-            port = Integer.parseInt(reader.readLine());
-          }
-        }
-        sendQuitRequest(port);
-        if (fileExists) {
-          deleteRecurse(Paths.get(path));
-          file.delete();
-        }
-        return;
-      default:
-        break;
+  private static int findAvailablePort() {
+    try (ServerSocket tempSocket = new ServerSocket(0)) {
+      return tempSocket.getLocalPort();
+    } catch (IOException e) {
+      return -1;
     }
   }
 
-  private static Map parseArgs(String[] args) {
-    Map parsedArgs = new HashMap();
-    for (String arg : args) {
-      if (arg.startsWith("--port=")) {
-        parsedArgs.put("port", arg.substring("--port=".length()));
-      } else if (arg.equals("START") || arg.equals("STOP")) {
-        parsedArgs.put("action", arg);
-      } else {
-        throw new RuntimeException("Only accepts START, STOP, and --port= as arguments");
-      }
-    }
-    if (parsedArgs.get("action") == null) {
-      throw new RuntimeException("EXPECTING START | STOP");
-    }
-    return parsedArgs;
+  /**
+   * Returns a {@link DatastoreOptions} instance that sets the host to use the Datastore emulator on
+   * localhost.
+   */
+  public DatastoreOptions options() {
+    return DatastoreOptions.builder()
+        .projectId(projectId)
+        .host("localhost:" + Integer.toString(port))
+        .authCredentials(AuthCredentials.noAuth())
+        .retryParams(RetryParams.noRetries())
+        .build();
   }
 
-  public static boolean isActive(String projectId, int port) {
-    try {
-      StringBuilder urlBuilder = new StringBuilder("http://localhost:").append(port);
-      urlBuilder.append("/datastore/v1beta2/datasets/").append(projectId).append("/lookup");
-      URL url = new URL(urlBuilder.toString());
-      try (BufferedReader reader =
-          new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) {
-        return "Valid RPC".equals(reader.readLine());
-      }
-    } catch (IOException ignore) {
-      // assume not active
-      return false;
+  /**
+   * Returns the project ID associated with this local Datastore emulator.
+   */
+  public String projectId() {
+    return projectId;
+  }
+
+  /**
+   * Returns the consistency setting for the local Datastore emulator.
+   */
+  public double consistency() {
+    return consistency;
+  }
+
+  /**
+   * Creates a local Datastore helper with the specified settings for project ID and consistency.
+   *
+   * @param consistency the fraction of Datastore writes that are immediately visible to global
+   *     queries, with 0.0 meaning no writes are immediately visible and 1.0 meaning all writes
+   *     are immediately visible. Note that setting this to 1.0 may mask incorrect assumptions
+   *     about the consistency of non-ancestor queries; non-ancestor queries are eventually
+   *     consistent.
+   */
+  public static LocalDatastoreHelper create(double consistency) {
+    LocalDatastoreHelper helper = new LocalDatastoreHelper(consistency);
+    return helper;
+  }
+
+  /**
+   * Creates a local Datastore helper with a placeholder project ID and the default consistency
+   * setting of 0.9. Consistency refers to the fraction of Datastore writes that are immediately
+   * visible to global queries, with 0.0 meaning no writes are immediately visible and 1.0 meaning
+   * all writes are immediately visible.
+   */
+  public static LocalDatastoreHelper create() {
+    return create(DEFAULT_CONSISTENCY);
+  }
+
+  /**
+   * Starts the local Datastore emulator. Leftover data from previous uses of the emulator will be
+   * removed.
+   *
+   * @throws InterruptedException if emulator-related tasks are interrupted
+   * @throws IOException if there are socket exceptions or issues creating/deleting the temporary
+   * data folder
+   */
+  public void start() throws IOException, InterruptedException {
+    // send a quick request in case we have a hanging process from a previous run
+    sendQuitRequest(port);
+    // Each run is associated with its own folder that is deleted once test completes.
+    gcdPath = Files.createTempDirectory("gcd");
+    File gcdFolder = gcdPath.toFile();
+    gcdFolder.deleteOnExit();
+
+    Path gcdExecutablePath;
+    // If cloud is available we use it, otherwise we download and start gcd
+    if (INSTALLED_GCD_PATH == null) {
+      downloadGcd();
+      gcdExecutablePath = gcdPath.resolve("gcd");
+    } else {
+      gcdExecutablePath = INSTALLED_GCD_PATH;
     }
+    startGcd(gcdExecutablePath, consistency);
   }
 }
diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/package-info.java b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/package-info.java
similarity index 75%
rename from gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/package-info.java
rename to gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/package-info.java
index d03c9d85cd09..1a00a8c13cf2 100644
--- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/testing/package-info.java
+++ b/gcloud-java-datastore/src/main/java/com/google/cloud/datastore/testing/package-info.java
@@ -20,20 +20,17 @@
  * 

A simple usage example: *

Before the test: *

 {@code
- * LocalGcdHelper gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT_NUMBER);
- * DatastoreOptions options = DatastoreOptions.builder()
- *     .projectId(PROJECT_ID)
- *     .host("localhost:8080")
- *     .build();
- * Datastore localDatastore = options.service();
+ * LocalDatastoreHelper helper = LocalDatastoreHelper.create();
+ * helper.start();
+ * Datastore localDatastore = helper.options().service();
  * } 
* *

After the test: *

 {@code
- * gcdHelper.stop();
+ * helper.stop();
  * } 
* * @see * gcloud-java tools for testing */ -package com.google.gcloud.datastore.testing; +package com.google.cloud.datastore.testing; diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java deleted file mode 100644 index 8cb41304500b..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.datastore; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.Function; -import com.google.common.collect.Lists; -import com.google.gcloud.datastore.BatchOption.ForceWrites; - -import java.util.List; -import java.util.Map; - - -class BatchImpl extends BaseDatastoreBatchWriter implements Batch { - - private final DatastoreImpl datastore; - private final boolean force; - - static class ResponseImpl implements Batch.Response { - - private final DatastoreV1.CommitResponse response; - - ResponseImpl(DatastoreV1.CommitResponse response) { - this.response = response; - } - - @Override - public List generatedKeys() { - return Lists.transform(response.getMutationResult().getInsertAutoIdKeyList(), - new Function() { - @Override public Key apply(DatastoreV1.Key keyPb) { - return Key.fromPb(keyPb); - } - }); - } - } - - BatchImpl(DatastoreImpl datastore, BatchOption... options) { - super("batch"); - this.datastore = datastore; - Map, BatchOption> optionsMap = BatchOption.asImmutableMap(options); - if (optionsMap.containsKey(ForceWrites.class)) { - force = ((ForceWrites) optionsMap.get(ForceWrites.class)).force(); - } else { - force = datastore.options().force(); - } - } - - @Override - public Batch.Response submit() { - validateActive(); - DatastoreV1.Mutation.Builder mutationPb = toMutationPb(); - if (force) { - mutationPb.setForce(force); - } - DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); - requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); - requestPb.setMutation(mutationPb); - DatastoreV1.CommitResponse responsePb = datastore.commit(requestPb.build()); - deactivate(); - return new ResponseImpl(responsePb); - } - - @Override - public Datastore datastore() { - return datastore; - } -} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchOption.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchOption.java deleted file mode 100644 index 362a74e96c79..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/BatchOption.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.datastore; - -import com.google.common.collect.ImmutableMap; - -import java.util.Map; - -public abstract class BatchOption implements java.io.Serializable { - - private static final long serialVersionUID = -3932758377282659839L; - - public static final class ForceWrites extends BatchOption { - - private static final long serialVersionUID = 2555054296046232799L; - - private final boolean force; - - public ForceWrites(boolean force) { - this.force = force; - } - - public boolean force() { - return force; - } - } - - BatchOption() { - // package protected - } - - public static ForceWrites forceWrites() { - return new ForceWrites(true); - } - - static Map, BatchOption> asImmutableMap(BatchOption... options) { - ImmutableMap.Builder, BatchOption> builder = - ImmutableMap.builder(); - for (BatchOption option : options) { - builder.put(option.getClass(), option); - } - return builder.build(); - } -} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java deleted file mode 100644 index 49a5728a4da9..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreImpl.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * 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.datastore; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import com.google.common.collect.AbstractIterator; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Sets; -import com.google.gcloud.BaseService; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryParams; -import com.google.gcloud.datastore.spi.DatastoreRpc; -import com.google.protobuf.ByteString; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; - -final class DatastoreImpl extends BaseService implements Datastore { - - private final DatastoreRpc datastoreRpc; - private final RetryParams retryParams; - - DatastoreImpl(DatastoreOptions options) { - super(options); - this.datastoreRpc = options.rpc(); - retryParams = MoreObjects.firstNonNull(options.retryParams(), RetryParams.noRetries()); - } - - @Override - public Batch newBatch(BatchOption... options) { - return new BatchImpl(this, options); - } - - @Override - public Transaction newTransaction(TransactionOption... options) { - return new TransactionImpl(this, options); - } - - @Override - public T runInTransaction(TransactionCallable callable, TransactionOption... options) { - return DatastoreHelper.runInTransaction(this, callable, options); - } - - @Override - public QueryResults run(Query query) { - return run(null, query); - } - - QueryResults run(DatastoreV1.ReadOptions readOptionsPb, Query query) { - return new QueryResultsImpl<>(this, readOptionsPb, query); - } - - DatastoreV1.RunQueryResponse runQuery(final DatastoreV1.RunQueryRequest requestPb) { - try { - return RetryHelper.runWithRetries(new Callable() { - @Override public DatastoreV1.RunQueryResponse call() throws DatastoreException { - return datastoreRpc.runQuery(requestPb); - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } - - @Override - public Key allocateId(IncompleteKey key) { - return DatastoreHelper.allocateId(this, key); - } - - @Override - public List allocateId(IncompleteKey... keys) { - if (keys.length == 0) { - return Collections.emptyList(); - } - DatastoreV1.AllocateIdsRequest.Builder requestPb = DatastoreV1.AllocateIdsRequest.newBuilder(); - for (IncompleteKey key : keys) { - requestPb.addKey(trimNameOrId(key).toPb()); - } - DatastoreV1.AllocateIdsResponse responsePb = allocateIds(requestPb.build()); - ImmutableList.Builder keyList = ImmutableList.builder(); - for (DatastoreV1.Key keyPb : responsePb.getKeyList()) { - keyList.add(Key.fromPb(keyPb)); - } - return keyList.build(); - } - - DatastoreV1.AllocateIdsResponse allocateIds(final DatastoreV1.AllocateIdsRequest requestPb) { - try { - return RetryHelper.runWithRetries(new Callable() { - @Override public DatastoreV1.AllocateIdsResponse call() throws DatastoreException { - return datastoreRpc.allocateIds(requestPb); - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } - - private IncompleteKey trimNameOrId(IncompleteKey key) { - if (key instanceof Key) { - return IncompleteKey.builder(key).build(); - } - return key; - } - - @Override - public Entity add(FullEntity entity) { - return DatastoreHelper.add(this, entity); - } - - @SuppressWarnings("unchecked") - @Override - public List add(FullEntity... entities) { - if (entities.length == 0) { - return Collections.emptyList(); - } - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - Map completeEntities = new LinkedHashMap<>(); - for (FullEntity entity : entities) { - Entity completeEntity = null; - if (entity.key() instanceof Key) { - completeEntity = Entity.convert((FullEntity) entity); - } - if (completeEntity != null) { - if (completeEntities.put(completeEntity.key(), completeEntity) != null) { - throw DatastoreException.throwInvalidRequest( - "Duplicate entity with the key %s", entity.key()); - } - mutationPb.addInsert(completeEntity.toPb()); - } else { - Preconditions.checkArgument(entity.hasKey(), "entity %s is missing a key", entity); - mutationPb.addInsertAutoId(entity.toPb()); - } - } - DatastoreV1.CommitResponse commitResponse = commitMutation(mutationPb); - Iterator allocatedKeys = - commitResponse.getMutationResult().getInsertAutoIdKeyList().iterator(); - ImmutableList.Builder responseBuilder = ImmutableList.builder(); - for (FullEntity entity : entities) { - Entity completeEntity = completeEntities.get(entity.key()); - if (completeEntity != null) { - responseBuilder.add(completeEntity); - } else { - responseBuilder.add(Entity.builder(Key.fromPb(allocatedKeys.next()), entity).build()); - } - } - return responseBuilder.build(); - } - - @Override - public Entity get(Key key) { - return DatastoreHelper.get(this, key); - } - - @Override - public Iterator get(Key... keys) { - return get(null, keys); - } - - @Override - public List fetch(Key... keys) { - return DatastoreHelper.fetch(this, keys); - } - - Iterator get(DatastoreV1.ReadOptions readOptionsPb, final Key... keys) { - if (keys.length == 0) { - return Collections.emptyIterator(); - } - DatastoreV1.LookupRequest.Builder requestPb = DatastoreV1.LookupRequest.newBuilder(); - if (readOptionsPb != null) { - requestPb.setReadOptions(readOptionsPb); - } - for (Key k : Sets.newLinkedHashSet(Arrays.asList(keys))) { - requestPb.addKey(k.toPb()); - } - return new ResultsIterator(requestPb); - } - - final class ResultsIterator extends AbstractIterator { - - private final DatastoreV1.LookupRequest.Builder requestPb; - Iterator iter; - - ResultsIterator(DatastoreV1.LookupRequest.Builder requestPb) { - this.requestPb = requestPb; - loadResults(); - } - - private void loadResults() { - DatastoreV1.LookupResponse responsePb = lookup(requestPb.build()); - iter = responsePb.getFoundList().iterator(); - requestPb.clearKey(); - if (responsePb.getDeferredCount() > 0) { - requestPb.addAllKey(responsePb.getDeferredList()); - } - } - - @SuppressWarnings("unchecked") - @Override - protected Entity computeNext() { - while (!iter.hasNext()) { - if (requestPb.getKeyCount() == 0) { - return endOfData(); - } - loadResults(); - } - return Entity.fromPb(iter.next().getEntity()); - } - } - - DatastoreV1.LookupResponse lookup(final DatastoreV1.LookupRequest requestPb) { - try { - return RetryHelper.runWithRetries(new Callable() { - @Override public DatastoreV1.LookupResponse call() throws DatastoreException { - return datastoreRpc.lookup(requestPb); - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } - - @SafeVarargs - @Override - public final void update(Entity... entities) { - if (entities.length > 0) { - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - Map dedupEntities = new LinkedHashMap<>(); - for (Entity entity : entities) { - dedupEntities.put(entity.key(), entity); - } - for (Entity entity : dedupEntities.values()) { - mutationPb.addUpdate(entity.toPb()); - } - commitMutation(mutationPb); - } - } - - @SafeVarargs - @Override - public final void put(Entity... entities) { - if (entities.length > 0) { - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - Map dedupEntities = new LinkedHashMap<>(); - for (Entity entity : entities) { - dedupEntities.put(entity.key(), entity); - } - for (Entity e : dedupEntities.values()) { - mutationPb.addUpsert(e.toPb()); - } - commitMutation(mutationPb); - } - } - - @Override - public void delete(Key... keys) { - if (keys.length > 0) { - DatastoreV1.Mutation.Builder mutationPb = DatastoreV1.Mutation.newBuilder(); - Set dedupKeys = new LinkedHashSet<>(Arrays.asList(keys)); - for (Key key : dedupKeys) { - mutationPb.addDelete(key.toPb()); - } - commitMutation(mutationPb); - } - } - - @Override - public KeyFactory newKeyFactory() { - return DatastoreHelper.newKeyFactory(options()); - } - - private DatastoreV1.CommitResponse commitMutation(DatastoreV1.Mutation.Builder mutationPb) { - if (options().force()) { - mutationPb.setForce(true); - } - DatastoreV1.CommitRequest.Builder requestPb = DatastoreV1.CommitRequest.newBuilder(); - requestPb.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL); - requestPb.setMutation(mutationPb); - return commit(requestPb.build()); - } - - DatastoreV1.CommitResponse commit(final DatastoreV1.CommitRequest requestPb) { - try { - return RetryHelper.runWithRetries(new Callable() { - @Override public DatastoreV1.CommitResponse call() throws DatastoreException { - return datastoreRpc.commit(requestPb); - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } - - ByteString requestTransactionId(DatastoreV1.BeginTransactionRequest.Builder requestPb) { - return beginTransaction(requestPb.build()).getTransaction(); - } - - DatastoreV1.BeginTransactionResponse beginTransaction( - final DatastoreV1.BeginTransactionRequest requestPb) { - try { - return RetryHelper.runWithRetries(new Callable() { - @Override - public DatastoreV1.BeginTransactionResponse call() throws DatastoreException { - return datastoreRpc.beginTransaction(requestPb); - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } - - void rollbackTransaction(ByteString transaction) { - DatastoreV1.RollbackRequest.Builder requestPb = DatastoreV1.RollbackRequest.newBuilder(); - requestPb.setTransaction(transaction); - rollback(requestPb.build()); - } - - void rollback(final DatastoreV1.RollbackRequest requestPb) { - try { - RetryHelper.runWithRetries(new Callable() { - @Override public Void call() throws DatastoreException { - datastoreRpc.rollback(requestPb); - return null; - } - }, retryParams, EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw DatastoreException.translateAndThrow(e); - } - } -} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java deleted file mode 100644 index 06282a2c79d1..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/ListValue.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.datastore; - -import static com.google.api.services.datastore.DatastoreV1.Value.LIST_VALUE_FIELD_NUMBER; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; - -import java.util.ArrayList; -import java.util.List; - -public final class ListValue extends Value>> { - - private static final long serialVersionUID = -5461475706792576395L; - - static final BaseMarshaller>, ListValue, Builder> MARSHALLER = - new BaseMarshaller>, ListValue, Builder>() { - - private static final long serialVersionUID = -3193794036327640106L; - - @Override - public int getProtoFieldId() { - return LIST_VALUE_FIELD_NUMBER; - } - - @Override - public Builder newBuilder(List> values) { - return builder().set(values); - } - - @Override - protected List> getValue(DatastoreV1.Value from) { - List> properties = new ArrayList<>(from.getListValueCount()); - for (DatastoreV1.Value valuePb : from.getListValueList()) { - properties.add(Value.fromPb(valuePb)); - } - return properties; - } - - @Override - protected void setValue(ListValue from, DatastoreV1.Value.Builder to) { - for (Value property : from.get()) { - to.addListValue(property.toPb()); - } - } - }; - - public static final class Builder extends - Value.BaseBuilder>, ListValue, Builder> { - - private ImmutableList.Builder> listBuilder = ImmutableList.builder(); - - private Builder() { - super(ValueType.LIST); - } - - private void addValueHelper(Value value) { - // see datastore_v1.proto definition for list_value - Preconditions.checkArgument(value.type() != ValueType.LIST, "Cannot contain another list"); - listBuilder.add(value); - } - - public Builder addValue(Value first, Value... other) { - addValueHelper(first); - for (Value value : other) { - addValueHelper(value); - } - return this; - } - - @Override - public Builder indexed(boolean indexed) { - // see issue #26 - throw DatastoreException.throwInvalidRequest("ListValue can't specify index"); - } - - /** - * Copy the list of values. - * - * @see com.google.gcloud.datastore.Value.BaseBuilder#set(java.lang.Object) - */ - @Override - public Builder set(List> values) { - listBuilder = ImmutableList.builder(); - for (Value value : values) { - addValue(value); - } - return this; - } - - @Override - public List> get() { - return listBuilder.build(); - } - - @Override - public ListValue build() { - Preconditions.checkState(!get().isEmpty(), "value list could not be empty"); - return new ListValue(this); - } - } - - public ListValue(List> values) { - this(builder().set(values)); - } - - public ListValue(Value first, Value... other) { - this(new Builder().addValue(first, other)); - } - - private ListValue(Builder builder) { - super(builder); - } - - @Override - public Builder toBuilder() { - return new Builder().mergeFrom(this); - } - - public static ListValue of(List> values) { - return new ListValue(values); - } - - public static ListValue of(Value first, Value... other) { - return new ListValue(first, other); - } - - public static Builder builder() { - return new Builder(); - } -} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionOption.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionOption.java deleted file mode 100644 index c1c8368213dc..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/TransactionOption.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.datastore; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.collect.ImmutableMap; - -import java.io.Serializable; -import java.util.Map; - - -public abstract class TransactionOption implements Serializable { - - private static final long serialVersionUID = -1862234444015690375L; - - public static final class ForceWrites extends TransactionOption { - - private static final long serialVersionUID = -6873967516988380886L; - - private final boolean force; - - public ForceWrites(boolean force) { - this.force = force; - } - - public boolean force() { - return force; - } - - @Override - BatchOption toBatchWriteOption() { - return new BatchOption.ForceWrites(force); - } - } - - public static final class IsolationLevel extends TransactionOption { - - private static final long serialVersionUID = -5592165378565409515L; - - private final Level level; - - public enum Level { - - SERIALIZABLE(DatastoreV1.BeginTransactionRequest.IsolationLevel.SERIALIZABLE), - SNAPSHOT(DatastoreV1.BeginTransactionRequest.IsolationLevel.SNAPSHOT); - - private final DatastoreV1.BeginTransactionRequest.IsolationLevel levelPb; - - Level(DatastoreV1.BeginTransactionRequest.IsolationLevel levelPb) { - this.levelPb = levelPb; - } - - DatastoreV1.BeginTransactionRequest.IsolationLevel toPb() { - return levelPb; - } - } - - public IsolationLevel(Level level) { - this.level = level; - } - - - public Level level() { - return level; - } - - @Override - BatchOption toBatchWriteOption() { - return null; - } - } - - TransactionOption() { - // package protected - } - - public static ForceWrites forceWrites() { - return new ForceWrites(true); - } - - public static IsolationLevel serializable() { - return new IsolationLevel(IsolationLevel.Level.SERIALIZABLE); - } - - public static IsolationLevel snapshot() { - return new IsolationLevel(IsolationLevel.Level.SNAPSHOT); - } - - static Map, TransactionOption> asImmutableMap( - TransactionOption... options) { - ImmutableMap.Builder, TransactionOption> builder = - ImmutableMap.builder(); - for (TransactionOption option : options) { - builder.put(option.getClass(), option); - } - return builder.build(); - } - - abstract BatchOption toBatchWriteOption(); -} diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DefaultDatastoreRpc.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DefaultDatastoreRpc.java deleted file mode 100644 index 093322fa4117..000000000000 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/spi/DefaultDatastoreRpc.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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.datastore.spi; - -import com.google.api.services.datastore.DatastoreV1.AllocateIdsRequest; -import com.google.api.services.datastore.DatastoreV1.AllocateIdsResponse; -import com.google.api.services.datastore.DatastoreV1.BeginTransactionRequest; -import com.google.api.services.datastore.DatastoreV1.BeginTransactionResponse; -import com.google.api.services.datastore.DatastoreV1.CommitRequest; -import com.google.api.services.datastore.DatastoreV1.CommitResponse; -import com.google.api.services.datastore.DatastoreV1.LookupRequest; -import com.google.api.services.datastore.DatastoreV1.LookupResponse; -import com.google.api.services.datastore.DatastoreV1.RollbackRequest; -import com.google.api.services.datastore.DatastoreV1.RollbackResponse; -import com.google.api.services.datastore.DatastoreV1.RunQueryRequest; -import com.google.api.services.datastore.DatastoreV1.RunQueryResponse; -import com.google.api.services.datastore.client.Datastore; -import com.google.api.services.datastore.client.DatastoreFactory; -import com.google.api.services.datastore.client.DatastoreOptions.Builder; -import com.google.common.base.Preconditions; -import com.google.gcloud.datastore.DatastoreException; -import com.google.gcloud.datastore.DatastoreOptions; - -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.URL; - -public class DefaultDatastoreRpc implements DatastoreRpc { - - private final Datastore client; - - public DefaultDatastoreRpc(DatastoreOptions options) { - String normalizedHost = normalizeHost(options.host()); - client = DatastoreFactory.get().create( - new Builder() - .dataset(options.projectId()) - .host(normalizedHost) - .initializer(options.httpRequestInitializer()) - .build()); - } - - private static String normalizeHost(String host) { - host = host.toLowerCase(); - if (includesScheme(host)) { - Preconditions.checkArgument(!(host.startsWith("https://") && isLocalHost(host)), - "\"https\" is not supported for localhost. Use \"http\" instead."); - return host; - } - return "http://" + host; - } - - private static boolean isLocalHost(String host) { - if (host != null) { - try { - String normalizedHost = host; - if (!includesScheme(normalizedHost)) { - normalizedHost = "http://" + normalizedHost; - } - InetAddress hostAddr = InetAddress.getByName(new URL(normalizedHost).getHost()); - return hostAddr.isAnyLocalAddress() || hostAddr.isLoopbackAddress(); - } catch (Exception e) { - // ignore - } - } - return false; - } - - private static boolean includesScheme(String url) { - return url.startsWith("http://") || url.startsWith("https://"); - } - - private static DatastoreException translate( - com.google.api.services.datastore.client.DatastoreException exception) { - String message = exception.getMessage(); - int code = exception.getCode(); - String reason = ""; - if (message != null) { - try { - JSONObject json = new JSONObject(new JSONTokener(message)); - JSONObject error = json.getJSONObject("error").getJSONArray("errors").getJSONObject(0); - reason = error.getString("reason"); - message = error.getString("message"); - } catch (JSONException ignore) { - // ignore - will be converted to unknown - } - } - if (reason == null) { - if (exception.getCause() instanceof IOException) { - return new DatastoreException((IOException) exception.getCause()); - } - } - return new DatastoreException(code, message, reason, exception); - } - - @Override - public AllocateIdsResponse allocateIds(AllocateIdsRequest request) { - try { - return client.allocateIds(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } - - @Override - public BeginTransactionResponse beginTransaction(BeginTransactionRequest request) { - try { - return client.beginTransaction(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } - - @Override - public CommitResponse commit(CommitRequest request) { - try { - return client.commit(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } - - @Override - public LookupResponse lookup(LookupRequest request) { - try { - return client.lookup(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } - - @Override - public RollbackResponse rollback(RollbackRequest request) { - try { - return client.rollback(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } - - @Override - public RunQueryResponse runQuery(RunQueryRequest request) { - try { - return client.runQuery(request); - } catch (com.google.api.services.datastore.client.DatastoreException ex) { - throw translate(ex); - } - } -} diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseDatastoreBatchWriterTest.java similarity index 59% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseDatastoreBatchWriterTest.java index ad8e0cee266a..6a30314082b7 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseDatastoreBatchWriterTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseDatastoreBatchWriterTest.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; -import com.google.api.services.datastore.DatastoreV1; import com.google.common.collect.ImmutableList; import org.easymock.EasyMock; @@ -29,6 +28,7 @@ import org.junit.Before; import org.junit.Test; +import java.util.LinkedList; import java.util.List; public class BaseDatastoreBatchWriterTest { @@ -84,15 +84,16 @@ public void tearDown() { public void testAdd() throws Exception { Entity entity2 = Entity.builder(ENTITY2).key(Key.builder(KEY1).name("name2").build()).build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addInsert(ENTITY1.toPb()) - .addInsert(entity2.toPb()) - .addInsert(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build().toPb()) - .addInsert(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build().toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setInsert(ENTITY1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build().toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build().toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setInsert(entity2.toPb()).build()); List entities = batchWriter .add(ENTITY1, INCOMPLETE_ENTITY_1, INCOMPLETE_ENTITY_2, entity2); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); assertEquals(ENTITY1, entities.get(0)); assertEquals(Entity.builder(KEY2, INCOMPLETE_ENTITY_1).build(), entities.get(1)); assertEquals(Entity.builder(KEY3, INCOMPLETE_ENTITY_2).build(), entities.get(2)); @@ -101,12 +102,11 @@ public void testAdd() throws Exception { @Test public void testAddAfterDelete() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(ENTITY1.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(ENTITY1.toPb()).build()); batchWriter.delete(KEY1); batchWriter.add(ENTITY1); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test(expected = DatastoreException.class) @@ -135,14 +135,15 @@ public void testAddWhenNotActive() throws Exception { @Test public void testAddWithDeferredAllocation() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addInsert(ENTITY1.toPb()) - .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb()) - .addInsertAutoId(INCOMPLETE_ENTITY_2.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(INCOMPLETE_ENTITY_1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(INCOMPLETE_ENTITY_2.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setInsert(ENTITY1.toPb()).build()); batchWriter.addWithDeferredIdAllocation(ENTITY1, INCOMPLETE_ENTITY_1); batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_2); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test(expected = DatastoreException.class) @@ -153,47 +154,43 @@ public void testAddWithDeferredAllocationWhenNotActive() throws Exception { @Test public void testUpdate() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpdate(ENTITY1.toPb()) - .addUpdate(ENTITY2.toPb()) - .addUpdate(ENTITY3.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(ENTITY1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(ENTITY2.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(ENTITY3.toPb()).build()); batchWriter.update(ENTITY1, ENTITY2); batchWriter.update(ENTITY3); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testUpdateAfterUpdate() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpdate(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpdate(entity.toPb()).build()); batchWriter.update(ENTITY1); batchWriter.update(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testUpdateAfterAdd() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.add(ENTITY1); batchWriter.update(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testUpdateAfterPut() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.put(ENTITY1); batchWriter.update(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test(expected = DatastoreException.class) @@ -210,58 +207,53 @@ public void testUpdateWhenNotActive() throws Exception { @Test public void testPut() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(ENTITY1.toPb()) - .addUpsert(ENTITY2.toPb()) - .addUpsert(ENTITY3.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(ENTITY1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(ENTITY2.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(ENTITY3.toPb()).build()); batchWriter.put(ENTITY1, ENTITY2); batchWriter.put(ENTITY3); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testPutAfterPut() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.put(ENTITY1); batchWriter.put(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testPutAfterAdd() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.add(ENTITY1); batchWriter.put(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testPutAfterUpdate() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.update(ENTITY1); batchWriter.put(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testPutAfterDelete() throws Exception { Entity entity = Entity.builder(ENTITY1).set("foo", "bar").build(); - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addUpsert(entity.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setUpsert(entity.toPb()).build()); batchWriter.delete(KEY1); batchWriter.put(entity); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test(expected = DatastoreException.class) @@ -272,46 +264,44 @@ public void testPutWhenNotActive() throws Exception { @Test public void testDelete() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addDelete(KEY1.toPb()) - .addDelete(KEY2.toPb()) - .addDelete(KEY3.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY2.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY3.toPb()).build()); batchWriter.delete(KEY1, KEY2); batchWriter.delete(KEY3); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testDeleteAfterAdd() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addInsertAutoId(INCOMPLETE_ENTITY_1.toPb()) - .addDelete(KEY1.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder() + .setInsert(INCOMPLETE_ENTITY_1.toPb()).build()); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY1.toPb()).build()); batchWriter.add(ENTITY1); batchWriter.addWithDeferredIdAllocation(INCOMPLETE_ENTITY_1); batchWriter.delete(KEY1); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } + @Test public void testDeleteAfterUpdate() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addDelete(KEY1.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY1.toPb()).build()); batchWriter.update(ENTITY1); batchWriter.delete(KEY1); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test public void testDeleteAfterPut() throws Exception { - DatastoreV1.Mutation pb = DatastoreV1.Mutation.newBuilder() - .addDelete(KEY1.toPb()) - .build(); + List pbs = new LinkedList<>(); + pbs.add(com.google.datastore.v1beta3.Mutation.newBuilder().setDelete(KEY1.toPb()).build()); batchWriter.put(ENTITY1); batchWriter.delete(KEY1); - assertEquals(pb, batchWriter.toMutationPb().build()); + assertEquals(pbs, batchWriter.toMutationPbList()); } @Test(expected = DatastoreException.class) diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseEntityTest.java similarity index 89% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseEntityTest.java index a69ea5e20e3b..f5100b14dac5 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseEntityTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseEntityTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -35,6 +35,7 @@ public class BaseEntityTest { private static final Blob BLOB = Blob.copyFrom(new byte[]{1, 2}); private static final DateTime DATE_TIME = DateTime.now(); + private static final LatLng LAT_LNG = new LatLng(37.422035, -122.084124); private static final Key KEY = Key.builder("ds1", "k1", "n1").build(); private static final Entity ENTITY = Entity.builder(KEY).set("name", "foo").build(); private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder("ds1", "k1").build(); @@ -62,9 +63,9 @@ public void setUp() { builder = new Builder(); builder.set("blob", BLOB).set("boolean", true).set("dateTime", DATE_TIME); builder.set("double", 1.25).set("key", KEY).set("string", "hello world"); - builder.set("long", 125).setNull("null").set("entity", ENTITY); + builder.set("long", 125).setNull("null").set("entity", ENTITY).set("latLng", LAT_LNG); builder.set("partialEntity", PARTIAL_ENTITY).set("stringValue", StringValue.of("bla")); - builder.set("list1", NullValue.of(), StringValue.of("foo")); + builder.set("list1", NullValue.of(), StringValue.of("foo"), LatLngValue.of(LAT_LNG)); builder.set("list2", ImmutableList.of(LongValue.of(10), DoubleValue.of(2))); builder.set("list3", Collections.singletonList(BooleanValue.of(true))); builder.set( @@ -77,6 +78,7 @@ public void setUp() { builder.set("entityList", ENTITY, PARTIAL_ENTITY); builder.set("stringList", "s1", "s2", "s3"); builder.set("longList", 1, 23, 456); + builder.set("latLngList", LAT_LNG, LAT_LNG); } @Test @@ -159,6 +161,12 @@ public void testGetDateTime() throws Exception { assertEquals(dateTime, entity.getDateTime("dateTime")); } + @Test + public void testGetLatLng() throws Exception { + BaseEntity entity = builder.build(); + assertEquals(LAT_LNG, entity.getLatLng("latLng")); + } + @Test public void testGetKey() throws Exception { BaseEntity entity = builder.build(); @@ -181,9 +189,10 @@ public void testGetEntity() throws Exception { public void testGetList() throws Exception { BaseEntity entity = builder.build(); List> list = entity.getList("list1"); - assertEquals(2, list.size()); + assertEquals(3, list.size()); assertEquals(NullValue.of(), list.get(0)); assertEquals("foo", list.get(1).get()); + assertEquals(LAT_LNG, list.get(2).get()); list = entity.getList("list2"); assertEquals(2, list.size()); assertEquals(Long.valueOf(10), list.get(0).get()); @@ -217,12 +226,13 @@ public void testGetBlob() throws Exception { @Test public void testNames() throws Exception { - Set names = ImmutableSet.builder() - .add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3") - .add("entity", "partialEntity", "null", "dateTime", "blob", "key", "blobList") - .add("booleanList", "dateTimeList", "doubleList", "keyList", "entityList", "stringList") - .add("longList") - .build(); + Set names = + ImmutableSet.builder() + .add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3") + .add("entity", "partialEntity", "null", "dateTime", "blob", "key", "blobList") + .add("booleanList", "dateTimeList", "doubleList", "keyList", "entityList", "stringList") + .add("longList", "latLng", "latLngList") + .build(); BaseEntity entity = builder.build(); assertEquals(names, entity.names()); } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseKeyTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseKeyTest.java similarity index 96% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseKeyTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseKeyTest.java index 43db4695b191..abbfbd3a2f16 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BaseKeyTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BaseKeyTest.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; @@ -83,7 +82,8 @@ public void testBadDatasetInSetter() throws Exception { public void testNamespace() throws Exception { Builder builder = new Builder("ds", "k"); BaseKey key = builder.build(); - assertNull(key.namespace()); + assertTrue(key.namespace() != null); + assertTrue(key.namespace().isEmpty()); key = builder.namespace("ns").build(); assertEquals("ns", key.namespace()); } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobTest.java index 1bb7c3fc476e..009c1a31ee1e 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobValueTest.java similarity index 80% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobValueTest.java index 40d0299d8fb3..3c6586939a54 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BlobValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BlobValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,19 +37,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { BlobValue value = BlobValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { BlobValue.Builder builder = BlobValue.builder(CONTENT); - BlobValue value = builder.meaning(1).indexed(false).build(); + BlobValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BooleanValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BooleanValueTest.java similarity index 79% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BooleanValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BooleanValueTest.java index 16bbe9cbf518..2750c70e992b 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/BooleanValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/BooleanValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -35,19 +35,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { BooleanValue value = BooleanValue.of(false); assertFalse(value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { BooleanValue.Builder builder = BooleanValue.builder(true); - BooleanValue value = builder.meaning(1).indexed(true).build(); + BooleanValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertTrue(value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertTrue(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/CursorTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/CursorTest.java similarity index 97% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/CursorTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/CursorTest.java index 72fd3d13cebe..34bdd59bf196 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/CursorTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/CursorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreExceptionTest.java similarity index 81% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreExceptionTest.java index f7bdcb89bcec..cf086ff25ba0 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreExceptionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -26,8 +26,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper; import org.junit.Test; @@ -38,29 +38,29 @@ public class DatastoreExceptionTest { @Test public void testDatastoreException() throws Exception { - DatastoreException exception = new DatastoreException(409, "message", "ABORTED"); - assertEquals(409, exception.code()); + DatastoreException exception = new DatastoreException(10, "message", "ABORTED"); + assertEquals(10, exception.code()); assertEquals("ABORTED", exception.reason()); assertEquals("message", exception.getMessage()); assertTrue(exception.retryable()); assertTrue(exception.idempotent()); - exception = new DatastoreException(403, "message", "DEADLINE_EXCEEDED"); - assertEquals(403, exception.code()); + exception = new DatastoreException(4, "message", "DEADLINE_EXCEEDED"); + assertEquals(4, exception.code()); assertEquals("DEADLINE_EXCEEDED", exception.reason()); assertEquals("message", exception.getMessage()); assertTrue(exception.retryable()); assertTrue(exception.idempotent()); - exception = new DatastoreException(503, "message", "UNAVAILABLE"); - assertEquals(503, exception.code()); + exception = new DatastoreException(14, "message", "UNAVAILABLE"); + assertEquals(14, exception.code()); assertEquals("UNAVAILABLE", exception.reason()); assertEquals("message", exception.getMessage()); assertTrue(exception.retryable()); assertTrue(exception.idempotent()); - exception = new DatastoreException(500, "message", "INTERNAL"); - assertEquals(500, exception.code()); + exception = new DatastoreException(2, "message", "INTERNAL"); + assertEquals(2, exception.code()); assertEquals("INTERNAL", exception.reason()); assertEquals("message", exception.getMessage()); assertFalse(exception.retryable()); @@ -77,7 +77,7 @@ public void testDatastoreException() throws Exception { @Test public void testTranslateAndThrow() throws Exception { - DatastoreException cause = new DatastoreException(503, "message", "UNAVAILABLE"); + DatastoreException cause = new DatastoreException(14, "message", "UNAVAILABLE"); RetryHelper.RetryHelperException exceptionMock = createMock(RetryHelper.RetryHelperException.class); expect(exceptionMock.getCause()).andReturn(cause).times(2); @@ -85,7 +85,7 @@ public void testTranslateAndThrow() throws Exception { try { DatastoreException.translateAndThrow(exceptionMock); } catch (BaseServiceException ex) { - assertEquals(503, ex.code()); + assertEquals(14, ex.code()); assertEquals("message", ex.getMessage()); assertTrue(ex.retryable()); assertTrue(ex.idempotent()); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreHelperTest.java similarity index 65% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreHelperTest.java index 55c8d0cf3ce6..2538a75847bd 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreHelperTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreHelperTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -27,8 +27,9 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.datastore.Datastore.TransactionCallable; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; -import com.google.gcloud.datastore.Datastore.TransactionCallable; import org.easymock.EasyMock; import org.junit.Test; @@ -66,52 +67,100 @@ public void testAllocateId() throws Exception { } @Test - public void testGet() throws Exception { + public void testGetWithDatastore() throws Exception { Datastore datastore = createStrictMock(Datastore.class); IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build(); Key key1 = Key.builder(pKey1, 1).build(); Entity entity1 = Entity.builder(key1).build(); Key key2 = Key.builder(pKey1, 2).build(); - expect(datastore.get(new Key[]{key1})) + ReadOption eventualConsistency = ReadOption.eventualConsistency(); + expect(datastore.get(Collections.singletonList(key1))) .andReturn(Collections.singletonList(entity1).iterator()); - expect(datastore.get(new Key[]{key2})) + expect(datastore.get(Collections.singletonList(key2))) .andReturn(Collections.emptyIterator()); + expect(datastore.get(Collections.singletonList(key1), eventualConsistency)) + .andReturn(Collections.singletonList(entity1).iterator()); replay(datastore); assertEquals(entity1, DatastoreHelper.get(datastore, key1)); assertNull(DatastoreHelper.get(datastore, key2)); + assertEquals(entity1, DatastoreHelper.get(datastore, key1, eventualConsistency)); verify(datastore); } + @Test + public void testGetWithTransaction() throws Exception { + Transaction transaction = createStrictMock(Transaction.class); + IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build(); + Key key1 = Key.builder(pKey1, 1).build(); + Entity entity1 = Entity.builder(key1).build(); + Key key2 = Key.builder(pKey1, 2).build(); + expect(transaction.get(new Key[] {key1})) + .andReturn(Collections.singletonList(entity1).iterator()); + expect(transaction.get(new Key[] {key2})).andReturn(Collections.emptyIterator()); + replay(transaction); + assertEquals(entity1, DatastoreHelper.get(transaction, key1)); + assertNull(DatastoreHelper.get(transaction, key2)); + verify(transaction); + } + @Test public void testAdd() throws Exception { Datastore datastore = createStrictMock(Datastore.class); IncompleteKey pKey = IncompleteKey.builder("ds", "k").build(); Key key = Key.builder(pKey, 1).build(); Entity entity = Entity.builder(key).build(); - expect(datastore.add(new Entity[]{entity})) - .andReturn(Collections.singletonList(entity)); + expect(datastore.add(new Entity[] {entity})).andReturn(Collections.singletonList(entity)); replay(datastore); assertEquals(entity, DatastoreHelper.add(datastore, entity)); verify(datastore); } @Test - public void testFetch() throws Exception { + public void testFetchWithDatastore() throws Exception { Datastore datastore = createStrictMock(Datastore.class); IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build(); Key key1 = Key.builder(pKey1, 1).build(); Key key2 = Key.builder(pKey1, "a").build(); Entity entity1 = Entity.builder(key1).build(); Entity entity2 = Entity.builder(key2).build(); - expect(datastore.get(key1, key2)).andReturn(Iterators.forArray(entity1, entity2)).once(); + ReadOption eventualConsistency = ReadOption.eventualConsistency(); + expect(datastore.get(ImmutableList.of(key1, key2))) + .andReturn(Iterators.forArray(entity1, entity2)) + .once(); + expect(datastore.get(ImmutableList.of(key1, key2), eventualConsistency)) + .andReturn(Iterators.forArray(entity1, entity2)) + .once(); replay(datastore); - List values = DatastoreHelper.fetch(datastore, key1, key2); + List values = DatastoreHelper.fetch(datastore, new Key[] {key1, key2}); + assertEquals(2, values.size()); + assertEquals(entity1, values.get(0)); + assertEquals(entity2, values.get(1)); + values = DatastoreHelper.fetch(datastore, new Key[] {key1, key2}, eventualConsistency); assertEquals(2, values.size()); assertEquals(entity1, values.get(0)); assertEquals(entity2, values.get(1)); verify(datastore); } + @Test + public void testFetchWithTransaction() throws Exception { + Transaction transaction = createStrictMock(Transaction.class); + IncompleteKey pKey1 = IncompleteKey.builder("ds", "k").build(); + Key key1 = Key.builder(pKey1, 1).build(); + Key key2 = Key.builder(pKey1, "a").build(); + Entity entity1 = Entity.builder(key1).build(); + Entity entity2 = Entity.builder(key2).build(); + expect(transaction.get(new Key[] {key1, key2})) + .andReturn(Iterators.forArray(entity1, entity2)) + .once(); + replay(transaction); + List values = DatastoreHelper.fetch(transaction, new Key[] {key1, key2}); + assertEquals(2, values.size()); + assertEquals(entity1, values.get(0)); + assertEquals(entity2, values.get(1)); + verify(transaction); + } + @Test public void testRunInTransaction() throws Exception { final Datastore datastore = createStrictMock(Datastore.class); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreOptionsTest.java similarity index 75% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreOptionsTest.java index 1d188c7f4e94..786f3ea79623 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreOptionsTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreOptionsTest.java @@ -14,17 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import com.google.gcloud.datastore.spi.DatastoreRpc; -import com.google.gcloud.datastore.spi.DatastoreRpcFactory; -import com.google.gcloud.datastore.testing.LocalGcdHelper; +import com.google.cloud.datastore.spi.DatastoreRpc; +import com.google.cloud.datastore.spi.DatastoreRpcFactory; import org.easymock.EasyMock; import org.junit.Before; @@ -32,8 +29,8 @@ public class DatastoreOptionsTest { - private static final String PROJECT_ID = "project_id"; - private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); + private static final String PROJECT_ID = "project-id"; + private static final int PORT = 8080; private DatastoreRpcFactory datastoreRpcFactory; private DatastoreRpc datastoreRpc; private DatastoreOptions.Builder options; @@ -43,7 +40,6 @@ public void setUp() { datastoreRpcFactory = EasyMock.createMock(DatastoreRpcFactory.class); datastoreRpc = EasyMock.createMock(DatastoreRpc.class); options = DatastoreOptions.builder() - .normalizeDataset(false) .serviceRpcFactory(datastoreRpcFactory) .projectId(PROJECT_ID) .host("http://localhost:" + PORT); @@ -65,16 +61,10 @@ public void testHost() throws Exception { @Test public void testNamespace() throws Exception { - assertNull(options.build().namespace()); + assertTrue(options.build().namespace().isEmpty()); assertEquals("ns1", options.namespace("ns1").build().namespace()); } - @Test - public void testForce() throws Exception { - assertFalse(options.build().force()); - assertTrue(options.force(true).build().force()); - } - @Test public void testDatastore() throws Exception { assertSame(datastoreRpc, options.build().rpc()); @@ -82,12 +72,11 @@ public void testDatastore() throws Exception { @Test public void testToBuilder() throws Exception { - DatastoreOptions original = options.namespace("ns1").force(true).build(); + DatastoreOptions original = options.namespace("ns1").build(); DatastoreOptions copy = original.toBuilder().build(); assertEquals(original.projectId(), copy.projectId()); assertEquals(original.namespace(), copy.namespace()); assertEquals(original.host(), copy.host()); - assertEquals(original.force(), copy.force()); assertEquals(original.retryParams(), copy.retryParams()); assertEquals(original.authCredentials(), copy.authCredentials()); } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreTest.java similarity index 75% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreTest.java index e3829a2e71ce..d73d6964e064 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DatastoreTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -25,22 +25,25 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.api.services.datastore.DatastoreV1; -import com.google.api.services.datastore.DatastoreV1.EntityResult; -import com.google.api.services.datastore.DatastoreV1.QueryResultBatch; -import com.google.api.services.datastore.DatastoreV1.RunQueryRequest; -import com.google.api.services.datastore.DatastoreV1.RunQueryResponse; +import com.google.cloud.RetryParams; +import com.google.cloud.datastore.Query.ResultType; +import com.google.cloud.datastore.StructuredQuery.OrderBy; +import com.google.cloud.datastore.StructuredQuery.PropertyFilter; +import com.google.cloud.datastore.spi.DatastoreRpc; +import com.google.cloud.datastore.spi.DatastoreRpcFactory; +import com.google.cloud.datastore.testing.LocalDatastoreHelper; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; -import com.google.gcloud.AuthCredentials; -import com.google.gcloud.RetryParams; -import com.google.gcloud.datastore.Query.ResultType; -import com.google.gcloud.datastore.StructuredQuery.OrderBy; -import com.google.gcloud.datastore.StructuredQuery.Projection; -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; -import com.google.gcloud.datastore.spi.DatastoreRpc; -import com.google.gcloud.datastore.spi.DatastoreRpcFactory; -import com.google.gcloud.datastore.testing.LocalGcdHelper; +import com.google.datastore.v1beta3.EntityResult; +import com.google.datastore.v1beta3.LookupRequest; +import com.google.datastore.v1beta3.LookupResponse; +import com.google.datastore.v1beta3.PartitionId; +import com.google.datastore.v1beta3.QueryResultBatch; +import com.google.datastore.v1beta3.ReadOptions; +import com.google.datastore.v1beta3.ReadOptions.ReadConsistency; +import com.google.datastore.v1beta3.RunQueryRequest; +import com.google.datastore.v1beta3.RunQueryResponse; import com.google.protobuf.ByteString; import org.easymock.EasyMock; @@ -64,13 +67,17 @@ @RunWith(JUnit4.class) public class DatastoreTest { - private static final String PROJECT_ID = LocalGcdHelper.DEFAULT_PROJECT_ID; + private static LocalDatastoreHelper helper = LocalDatastoreHelper.create(1.0); + private static final DatastoreOptions options = helper.options(); + private static final Datastore datastore = options.service(); + private static final String PROJECT_ID = options.projectId(); private static final String KIND1 = "kind1"; private static final String KIND2 = "kind2"; private static final String KIND3 = "kind3"; private static final NullValue NULL_VALUE = NullValue.of(); private static final StringValue STR_VALUE = StringValue.of("str"); - private static final BooleanValue BOOL_VALUE = BooleanValue.builder(false).indexed(false).build(); + private static final BooleanValue BOOL_VALUE = BooleanValue.builder(false) + .excludeFromIndexes(true).build(); private static final IncompleteKey INCOMPLETE_KEY1 = IncompleteKey.builder(PROJECT_ID, KIND1).build(); private static final IncompleteKey INCOMPLETE_KEY2 = @@ -86,7 +93,10 @@ public class DatastoreTest { .addValue(STR_VALUE, BOOL_VALUE) .build(); private static final ListValue LIST_VALUE2 = ListValue.of(Collections.singletonList(KEY_VALUE)); + private static final ListValue EMPTY_LIST_VALUE = ListValue.of(Collections.>emptyList()); private static final DateTimeValue DATE_TIME_VALUE = new DateTimeValue(DateTime.now()); + private static final LatLngValue LAT_LNG_VALUE = + new LatLngValue(new LatLng(37.422035, -122.084124)); private static final FullEntity PARTIAL_ENTITY1 = FullEntity.builder(INCOMPLETE_KEY2).set("str", STR_VALUE).set("bool", BOOL_VALUE) .set("list", LIST_VALUE1).build(); @@ -99,40 +109,39 @@ public class DatastoreTest { private static final Entity ENTITY1 = Entity.builder(KEY1) .set("str", STR_VALUE) .set("date", DATE_TIME_VALUE) + .set("latLng", LAT_LNG_VALUE) .set("bool", BOOL_VALUE) .set("partial1", EntityValue.of(PARTIAL_ENTITY1)) .set("list", LIST_VALUE2) + .set("emptyList", EMPTY_LIST_VALUE) .build(); private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str") .set("name", "Dan").setNull("null").set("age", 20).build(); private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str") .set("null", NULL_VALUE).set("partial1", PARTIAL_ENTITY2).set("partial2", ENTITY2).build(); - private DatastoreOptions options; - private Datastore datastore; - - private static LocalGcdHelper gcdHelper; - private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); + private DatastoreOptions rpcMockOptions; + private DatastoreRpcFactory rpcFactoryMock; + private DatastoreRpc rpcMock; @Rule public ExpectedException thrown = ExpectedException.none(); @BeforeClass public static void beforeClass() throws IOException, InterruptedException { - if (!LocalGcdHelper.isActive(PROJECT_ID, PORT)) { - gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT, 1.0); - } + helper.start(); } @Before public void setUp() { - options = DatastoreOptions.builder() - .projectId(PROJECT_ID) - .host("http://localhost:" + PORT) - .authCredentials(AuthCredentials.noAuth()) - .retryParams(RetryParams.noRetries()) + rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); + rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); + rpcMockOptions = options + .toBuilder() + .retryParams(RetryParams.defaultInstance()) + .serviceRpcFactory(rpcFactoryMock) .build(); - datastore = options.service(); + EasyMock.expect(rpcFactoryMock.create(rpcMockOptions)).andReturn(rpcMock); StructuredQuery query = Query.keyQueryBuilder().build(); QueryResults result = datastore.run(query); datastore.delete(Iterators.toArray(result, Key.class)); @@ -141,9 +150,7 @@ public void setUp() { @AfterClass public static void afterClass() throws IOException, InterruptedException { - if (gcdHelper != null) { - gcdHelper.stop(); - } + helper.stop(); } @Test @@ -423,6 +430,26 @@ public void testRunGqlQueryWithCasting() { assertFalse(results3.hasNext()); } + @Test + public void testGqlQueryPagination() throws DatastoreException { + List responses = buildResponsesForQueryPagination(); + for (int i = 0; i < responses.size(); i++) { + EasyMock.expect(rpcMock.runQuery(EasyMock.anyObject(RunQueryRequest.class))) + .andReturn(responses.get(i)); + } + EasyMock.replay(rpcFactoryMock, rpcMock); + Datastore mockDatastore = rpcMockOptions.service(); + QueryResults results = + mockDatastore.run(Query.gqlQueryBuilder(ResultType.KEY, "select __key__ from *").build()); + int count = 0; + while (results.hasNext()) { + count += 1; + results.next(); + } + assertEquals(count, 5); + EasyMock.verify(rpcFactoryMock, rpcMock); + } + @Test public void testRunStructuredQuery() { Query query = @@ -440,7 +467,7 @@ public void testRunStructuredQuery() { StructuredQuery keyOnlyProjectionQuery = Query.projectionEntityQueryBuilder() - .kind(KIND1).projection(Projection.property("__key__")).build(); + .kind(KIND1).projection("__key__").build(); QueryResults results3 = datastore.run(keyOnlyProjectionQuery); assertTrue(results3.hasNext()); ProjectionEntity projectionEntity = results3.next(); @@ -450,9 +477,9 @@ public void testRunStructuredQuery() { StructuredQuery projectionQuery = Query.projectionEntityQueryBuilder() .kind(KIND2) - .projection(Projection.property("age"), Projection.first("name")) + .projection("age") .filter(PropertyFilter.gt("age", 18)) - .groupBy("age") + .distinctOn("age") .orderBy(OrderBy.asc("age")) .limit(10) .build(); @@ -462,17 +489,68 @@ public void testRunStructuredQuery() { ProjectionEntity entity = results4.next(); assertEquals(ENTITY2.key(), entity.key()); assertEquals(20, entity.getLong("age")); - assertEquals("Dan", entity.getString("name")); - assertEquals(2, entity.properties().size()); + assertEquals(1, entity.properties().size()); assertFalse(results4.hasNext()); } @Test + public void testStructuredQueryPagination() throws DatastoreException { + List responses = buildResponsesForQueryPagination(); + for (int i = 0; i < responses.size(); i++) { + EasyMock.expect(rpcMock.runQuery(EasyMock.anyObject(RunQueryRequest.class))) + .andReturn(responses.get(i)); + } + EasyMock.replay(rpcFactoryMock, rpcMock); + Datastore datastore = rpcMockOptions.service(); + QueryResults results = datastore.run(Query.keyQueryBuilder().build()); + int count = 0; + while (results.hasNext()) { + count += 1; + results.next(); + } + assertEquals(count, 5); + EasyMock.verify(rpcFactoryMock, rpcMock); + } + + private List buildResponsesForQueryPagination() { + Entity entity4 = Entity.builder(KEY4).set("value", StringValue.of("value")).build(); + Entity entity5 = Entity.builder(KEY5).set("value", "value").build(); + datastore.add(ENTITY3, entity4, entity5); + List responses = new ArrayList<>(); + Query query = Query.keyQueryBuilder().build(); + RunQueryRequest.Builder requestPb = RunQueryRequest.newBuilder(); + query.populatePb(requestPb); + QueryResultBatch queryResultBatchPb = RunQueryResponse.newBuilder() + .mergeFrom(((DatastoreImpl) datastore).runQuery(requestPb.build())) + .getBatch(); + QueryResultBatch queryResultBatchPb1 = QueryResultBatch.newBuilder() + .mergeFrom(queryResultBatchPb) + .setMoreResults(QueryResultBatch.MoreResultsType.NOT_FINISHED) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(0, 1)) + .setEndCursor(queryResultBatchPb.getEntityResultsList().get(0).getCursor()) + .build(); + responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb1).build()); + QueryResultBatch queryResultBatchPb2 = QueryResultBatch.newBuilder() + .mergeFrom(queryResultBatchPb) + .setMoreResults(QueryResultBatch.MoreResultsType.NOT_FINISHED) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(1, 3)) + .setEndCursor(queryResultBatchPb.getEntityResultsList().get(2).getCursor()) + .build(); + responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb2).build()); + QueryResultBatch queryResultBatchPb3 = QueryResultBatch.newBuilder() + .mergeFrom(queryResultBatchPb) + .setMoreResults(QueryResultBatch.MoreResultsType.NO_MORE_RESULTS) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(3, 5)) + .setEndCursor(queryResultBatchPb.getEntityResultsList().get(4).getCursor()) + .build(); + responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb3).build()); + return responses; + } + public void testQueryPaginationWithLimit() throws DatastoreException { - DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); - DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); - EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class))) - .andReturn(rpcMock); List responses = buildResponsesForQueryPaginationWithLimit(); List endCursors = Lists.newArrayListWithCapacity(responses.size()); for (RunQueryResponse response : responses) { @@ -483,17 +561,13 @@ public void testQueryPaginationWithLimit() throws DatastoreException { } } EasyMock.replay(rpcFactoryMock, rpcMock); - Datastore mockDatastore = options.toBuilder() - .retryParams(RetryParams.defaultInstance()) - .serviceRpcFactory(rpcFactoryMock) - .build() - .service(); + Datastore datastore = rpcMockOptions.service(); int limit = 2; int totalCount = 0; Iterator cursorIter = endCursors.iterator(); StructuredQuery query = Query.entityQueryBuilder().limit(limit).build(); while (true) { - QueryResults results = mockDatastore.run(query); + QueryResults results = datastore.run(query); int resultCount = 0; while (results.hasNext()) { results.next(); @@ -527,16 +601,16 @@ private List buildResponsesForQueryPaginationWithLimit() { QueryResultBatch queryResultBatchPb1 = QueryResultBatch.newBuilder() .mergeFrom(queryResultBatchPb) .setMoreResults(QueryResultBatch.MoreResultsType.NOT_FINISHED) - .clearEntityResult() - .addAllEntityResult(queryResultBatchPb.getEntityResultList().subList(0, 1)) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(0, 1)) .setEndCursor(ByteString.copyFromUtf8("a")) .build(); responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb1).build()); QueryResultBatch queryResultBatchPb2 = QueryResultBatch.newBuilder() .mergeFrom(queryResultBatchPb) .setMoreResults(QueryResultBatch.MoreResultsType.MORE_RESULTS_AFTER_LIMIT) - .clearEntityResult() - .addAllEntityResult(queryResultBatchPb.getEntityResultList().subList(1, 2)) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(1, 2)) .setEndCursor( ByteString.copyFrom(new byte[] {(byte) 0x80})) // test invalid UTF-8 string .build(); @@ -544,22 +618,43 @@ private List buildResponsesForQueryPaginationWithLimit() { QueryResultBatch queryResultBatchPb3 = QueryResultBatch.newBuilder() .mergeFrom(queryResultBatchPb) .setMoreResults(QueryResultBatch.MoreResultsType.MORE_RESULTS_AFTER_LIMIT) - .clearEntityResult() - .addAllEntityResult(queryResultBatchPb.getEntityResultList().subList(2, 4)) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(2, 4)) .setEndCursor(ByteString.copyFromUtf8("b")) .build(); responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb3).build()); QueryResultBatch queryResultBatchPb4 = QueryResultBatch.newBuilder() .mergeFrom(queryResultBatchPb) .setMoreResults(QueryResultBatch.MoreResultsType.NO_MORE_RESULTS) - .clearEntityResult() - .addAllEntityResult(queryResultBatchPb.getEntityResultList().subList(4, 5)) + .clearEntityResults() + .addAllEntityResults(queryResultBatchPb.getEntityResultsList().subList(4, 5)) .setEndCursor(ByteString.copyFromUtf8("c")) .build(); responses.add(RunQueryResponse.newBuilder().setBatch(queryResultBatchPb4).build()); return responses; } + @Test + public void testEventualConsistencyQuery() { + ReadOptions readOption = + ReadOptions.newBuilder().setReadConsistencyValue(ReadConsistency.EVENTUAL_VALUE).build(); + com.google.datastore.v1beta3.GqlQuery query = com.google.datastore.v1beta3.GqlQuery.newBuilder() + .setQueryString("FROM * SELECT *") + .build(); + RunQueryRequest.Builder expectedRequest = RunQueryRequest.newBuilder() + .setReadOptions(readOption) + .setGqlQuery(query) + .setPartitionId(PartitionId.newBuilder().setProjectId(PROJECT_ID) + .build()); + EasyMock.expect(rpcMock.runQuery(expectedRequest.build())) + .andReturn(RunQueryResponse.newBuilder().build()); + EasyMock.replay(rpcFactoryMock, rpcMock); + Datastore datastore = rpcMockOptions.service(); + datastore.run( + Query.gqlQueryBuilder("FROM * SELECT *").build(), ReadOption.eventualConsistency()); + EasyMock.verify(rpcFactoryMock, rpcMock); + } + @Test public void testToUrlSafe() { byte[][] invalidUtf8 = @@ -627,12 +722,38 @@ public void testGet() { assertEquals(LIST_VALUE2, value3); DateTimeValue value4 = entity.getValue("date"); assertEquals(DATE_TIME_VALUE, value4); - FullEntity value5 = entity.getEntity("partial1"); - assertEquals(PARTIAL_ENTITY1, value5); - assertEquals(5, entity.names().size()); + LatLngValue value5 = entity.getValue("latLng"); + assertEquals(LAT_LNG_VALUE, value5); + FullEntity value6 = entity.getEntity("partial1"); + assertEquals(PARTIAL_ENTITY1, value6); + ListValue value7 = entity.getValue("emptyList"); + assertEquals(EMPTY_LIST_VALUE, value7); + assertEquals(7, entity.names().size()); assertFalse(entity.contains("bla")); } + @Test + public void testLookupEventualConsistency() { + ReadOptions readOption = + ReadOptions.newBuilder().setReadConsistencyValue(ReadConsistency.EVENTUAL_VALUE).build(); + com.google.datastore.v1beta3.Key key = com.google.datastore.v1beta3.Key.newBuilder() + .setPartitionId(PartitionId.newBuilder().setProjectId(PROJECT_ID).build()) + .addPath(com.google.datastore.v1beta3.Key.PathElement.newBuilder() + .setKind("kind1").setName("name").build()) + .build(); + LookupRequest lookupRequest = + LookupRequest.newBuilder().setReadOptions(readOption).addKeys(key).build(); + EasyMock.expect(rpcMock.lookup(lookupRequest)) + .andReturn(LookupResponse.newBuilder().build()) + .times(3); + EasyMock.replay(rpcFactoryMock, rpcMock); + Datastore datastore = rpcMockOptions.service(); + datastore.get(KEY1, ReadOption.eventualConsistency()); + datastore.get(ImmutableList.of(KEY1), ReadOption.eventualConsistency()); + datastore.fetch(ImmutableList.of(KEY1), ReadOption.eventualConsistency()); + EasyMock.verify(rpcFactoryMock, rpcMock); + } + @Test public void testGetArrayNoDeferredResults() { datastore.put(ENTITY3); @@ -651,7 +772,9 @@ public void testGetArrayNoDeferredResults() { assertEquals(PARTIAL_ENTITY2, partial1); assertEquals(ENTITY2, partial2); assertEquals(ValueType.BOOLEAN, entity3.getValue("bool").type()); - assertEquals(6, entity3.names().size()); + assertEquals(LAT_LNG_VALUE, entity3.getValue("latLng")); + assertEquals(EMPTY_LIST_VALUE, entity3.getValue("emptyList")); + assertEquals(8, entity3.names().size()); assertFalse(entity3.contains("bla")); try { entity3.getString("str"); @@ -689,26 +812,26 @@ public void testFetchArrayDeferredResults() throws DatastoreException { } private Datastore createDatastoreForDeferredLookup() throws DatastoreException { - List keysPb = new ArrayList<>(); + List keysPb = new ArrayList<>(); keysPb.add(KEY1.toPb()); keysPb.add(KEY2.toPb()); keysPb.add(KEY3.toPb()); keysPb.add(KEY4.toPb()); keysPb.add(KEY5.toPb()); - List lookupRequests = new ArrayList<>(); - lookupRequests.add(DatastoreV1.LookupRequest.newBuilder().addAllKey(keysPb).build()); + List lookupRequests = new ArrayList<>(); + lookupRequests.add(LookupRequest.newBuilder().addAllKeys(keysPb).build()); lookupRequests.add( - DatastoreV1.LookupRequest.newBuilder() - .addKey(keysPb.get(2)) - .addKey(keysPb.get(3)) - .addKey(keysPb.get(5)) + LookupRequest.newBuilder() + .addKeys(keysPb.get(2)) + .addKeys(keysPb.get(3)) + .addKeys(keysPb.get(5)) .build()); - lookupRequests.add(DatastoreV1.LookupRequest.newBuilder().addKey(keysPb.get(5)).build()); + lookupRequests.add(LookupRequest.newBuilder().addKeys(keysPb.get(5)).build()); Entity entity4 = Entity.builder(KEY4).set("value", StringValue.of("value")).build(); Entity entity5 = Entity.builder(KEY5).set("value", "value").build(); - List lookupResponses = new ArrayList<>(); + List lookupResponses = new ArrayList<>(); lookupResponses.add( - DatastoreV1.LookupResponse.newBuilder() + LookupResponse.newBuilder() .addFound(EntityResult.newBuilder().setEntity(ENTITY1.toPb())) .addFound(EntityResult.newBuilder().setEntity(entity4.toPb())) .addDeferred(keysPb.get(2)) @@ -716,29 +839,20 @@ private Datastore createDatastoreForDeferredLookup() throws DatastoreException { .addDeferred(keysPb.get(5)) .build()); lookupResponses.add( - DatastoreV1.LookupResponse.newBuilder() + LookupResponse.newBuilder() .addFound(EntityResult.newBuilder().setEntity(ENTITY3.toPb())) .addFound(EntityResult.newBuilder().setEntity(entity4.toPb())) .addDeferred(keysPb.get(5)) .build()); lookupResponses.add( - DatastoreV1.LookupResponse.newBuilder() + LookupResponse.newBuilder() .addFound(EntityResult.newBuilder().setEntity(entity5.toPb())) .build()); - DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); - DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); - EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class))) - .andReturn(rpcMock); for (int i = 0; i < lookupRequests.size(); i++) { EasyMock.expect(rpcMock.lookup(lookupRequests.get(i))).andReturn(lookupResponses.get(i)); } EasyMock.replay(rpcFactoryMock, rpcMock); - DatastoreOptions options = - this.options.toBuilder() - .retryParams(RetryParams.defaultInstance()) - .serviceRpcFactory(rpcFactoryMock) - .build(); - return options.service(); + return rpcMockOptions.service(); } @Test @@ -766,7 +880,6 @@ public void testAddEntity() { assertNotNull(datastore.get(entities.get(2).key())); } - @Test public void testUpdate() { List keys = datastore.fetch(ENTITY1.key(), ENTITY3.key()); @@ -835,23 +948,15 @@ public void testKeyFactory() { @Test public void testRetryableException() throws Exception { - DatastoreV1.LookupRequest requestPb = - DatastoreV1.LookupRequest.newBuilder().addKey(KEY1.toPb()).build(); - DatastoreV1.LookupResponse responsePb = DatastoreV1.LookupResponse.newBuilder() - .addFound(EntityResult.newBuilder().setEntity(ENTITY1.toPb())).build(); - DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); - DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); - EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class))) - .andReturn(rpcMock); + LookupRequest requestPb = LookupRequest.newBuilder().addKeys(KEY1.toPb()).build(); + LookupResponse responsePb = LookupResponse.newBuilder() + .addFound(EntityResult.newBuilder().setEntity(ENTITY1.toPb())) + .build(); EasyMock.expect(rpcMock.lookup(requestPb)) - .andThrow(new DatastoreException(503, "UNAVAILABLE", "UNAVAILABLE", null)) + .andThrow(new DatastoreException(14, "UNAVAILABLE", "UNAVAILABLE", null)) .andReturn(responsePb); EasyMock.replay(rpcFactoryMock, rpcMock); - DatastoreOptions options = this.options.toBuilder() - .retryParams(RetryParams.defaultInstance()) - .serviceRpcFactory(rpcFactoryMock) - .build(); - Datastore datastore = options.service(); + Datastore datastore = rpcMockOptions.service(); Entity entity = datastore.get(KEY1); assertEquals(ENTITY1, entity); EasyMock.verify(rpcFactoryMock, rpcMock); @@ -859,23 +964,13 @@ public void testRetryableException() throws Exception { @Test public void testNonRetryableException() throws Exception { - DatastoreV1.LookupRequest requestPb = - DatastoreV1.LookupRequest.newBuilder().addKey(KEY1.toPb()).build(); - DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); - DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); - EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class))) - .andReturn(rpcMock); + LookupRequest requestPb = LookupRequest.newBuilder().addKeys(KEY1.toPb()).build(); EasyMock.expect(rpcMock.lookup(requestPb)) .andThrow( new DatastoreException(DatastoreException.UNKNOWN_CODE, "denied", "PERMISSION_DENIED")) .times(1); EasyMock.replay(rpcFactoryMock, rpcMock); - RetryParams retryParams = RetryParams.builder().retryMinAttempts(2).build(); - DatastoreOptions options = this.options.toBuilder() - .retryParams(retryParams) - .serviceRpcFactory(rpcFactoryMock) - .build(); - Datastore datastore = options.service(); + Datastore datastore = rpcMockOptions.service(); thrown.expect(DatastoreException.class); thrown.expectMessage("denied"); datastore.get(KEY1); @@ -884,21 +979,11 @@ public void testNonRetryableException() throws Exception { @Test public void testRuntimeException() throws Exception { - DatastoreV1.LookupRequest requestPb = - DatastoreV1.LookupRequest.newBuilder().addKey(KEY1.toPb()).build(); - DatastoreRpcFactory rpcFactoryMock = EasyMock.createStrictMock(DatastoreRpcFactory.class); - DatastoreRpc rpcMock = EasyMock.createStrictMock(DatastoreRpc.class); - EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DatastoreOptions.class))) - .andReturn(rpcMock); + LookupRequest requestPb = LookupRequest.newBuilder().addKeys(KEY1.toPb()).build(); String exceptionMessage = "Artificial runtime exception"; - EasyMock.expect(rpcMock.lookup(requestPb)) - .andThrow(new RuntimeException(exceptionMessage)); + EasyMock.expect(rpcMock.lookup(requestPb)).andThrow(new RuntimeException(exceptionMessage)); EasyMock.replay(rpcFactoryMock, rpcMock); - DatastoreOptions options = this.options.toBuilder() - .retryParams(RetryParams.defaultInstance()) - .serviceRpcFactory(rpcFactoryMock) - .build(); - Datastore datastore = options.service(); + Datastore datastore = rpcMockOptions.service(); thrown.expect(DatastoreException.class); thrown.expectMessage(exceptionMessage); datastore.get(KEY1); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeTest.java index 23f3951a5dc3..34c2f7b7e29f 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeValueTest.java similarity index 80% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeValueTest.java index d7fef2ca69b9..8262e97241fa 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DateTimeValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DateTimeValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,19 +37,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { DateTimeValue value = DateTimeValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { DateTimeValue.Builder builder = DateTimeValue.builder(CONTENT); - DateTimeValue value = builder.meaning(1).indexed(false).build(); + DateTimeValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DoubleValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DoubleValueTest.java similarity index 80% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DoubleValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DoubleValueTest.java index fa39511a45de..ecc80fd14454 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DoubleValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/DoubleValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,19 +37,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { DoubleValue value = DoubleValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { DoubleValue.Builder builder = DoubleValue.builder(CONTENT); - DoubleValue value = builder.meaning(1).indexed(false).build(); + DoubleValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityTest.java index 30bdf16d9397..c3aad6f285ff 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityValueTest.java similarity index 74% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityValueTest.java index cd1f7af38067..07fb3bb0d5cf 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/EntityValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/EntityValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -38,25 +38,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { EntityValue value = EntityValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); - assertFalse(value.hasMeaning()); - } - - @Test(expected = IllegalArgumentException.class) - public void testIndexedNotAllowed() { - EntityValue.builder(CONTENT).indexed(true); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { EntityValue.Builder builder = EntityValue.builder(CONTENT); - EntityValue value = builder.meaning(1).indexed(false).build(); + EntityValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/FullEntityTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/FullEntityTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/FullEntityTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/FullEntityTest.java index 1d62c7a6dfae..c1a47212e422 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/FullEntityTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/FullEntityTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/IncompleteKeyTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/IncompleteKeyTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/IncompleteKeyTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/IncompleteKeyTest.java index acd1dfd3c9e3..f4562901d5c6 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/IncompleteKeyTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/IncompleteKeyTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyFactoryTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyFactoryTest.java similarity index 94% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyFactoryTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyFactoryTest.java index 92851bd87efe..93f08de11be8 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyFactoryTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyFactoryTest.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.Before; @@ -55,7 +54,7 @@ public void testReset() { key = keyFactory.newKey(); assertEquals("k1", key.kind()); assertEquals(PROJECT_ID, key.projectId()); - assertNull(key.namespace()); + assertTrue(key.namespace().isEmpty()); assertTrue(key.ancestors().isEmpty()); keyFactory = new KeyFactory(PROJECT_ID, "ns1").kind("k"); @@ -75,9 +74,9 @@ public void testReset() { @Test public void testNewKey() throws Exception { Key key = keyFactory.newKey(1); - verifyKey(key, 1L, null); + verifyKey(key, 1L, ""); key = keyFactory.newKey("n"); - verifyKey(key, "n", null); + verifyKey(key, "n", ""); PathElement p1 = PathElement.of("k1", "n"); PathElement p2 = PathElement.of("k2", 10); key = keyFactory.namespace("ns").ancestors(p1, p2).newKey("k3"); @@ -87,7 +86,7 @@ public void testNewKey() throws Exception { @Test public void testNewIncompleteKey() throws Exception { IncompleteKey key = keyFactory.newKey(); - verifyIncompleteKey(key, null); + verifyIncompleteKey(key, ""); PathElement p1 = PathElement.of("k1", "n"); PathElement p2 = PathElement.of("k2", 10); key = keyFactory.namespace("ns").ancestors(p1, p2).newKey(); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyTest.java index 1fdcc5394e7e..398ea22d01e4 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyValueTest.java similarity index 80% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyValueTest.java index 131a80462a62..0d2670b48c88 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/KeyValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/KeyValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,19 +37,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { KeyValue value = KeyValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { KeyValue.Builder builder = KeyValue.builder(CONTENT); - KeyValue value = builder.meaning(1).indexed(false).build(); + KeyValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngTest.java new file mode 100644 index 000000000000..401d8cc0c4c8 --- /dev/null +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngTest.java @@ -0,0 +1,77 @@ +/* + * 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.cloud.datastore; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class LatLngTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private static LatLng gp1 = new LatLng(37.422035, -122.084124); + private static LatLng gp2 = new LatLng(0.0, 0.0); + + private static final String INVALID_LAT_MESSAGE = + "latitude must be in the range [-90, 90] degrees"; + private static final String INVALID_LNG_MESSAGE = + "latitude must be in the range [-180, 180] degrees"; + + @Test + public void testEquals() { + assertEquals(gp1, gp1); + assertNotEquals(gp1, gp2); + } + + @Test + public void testUpperLatRange() { + new LatLng(90, 0); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(INVALID_LAT_MESSAGE); + new LatLng(91, 0); + } + + @Test + public void testLowerLatRange() { + new LatLng(-90, 0); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(INVALID_LAT_MESSAGE); + new LatLng(-91, 0); + } + + @Test + public void testUpperLngRange() { + new LatLng(0, 180); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(INVALID_LNG_MESSAGE); + new LatLng(0, 181); + } + + @Test + public void testLowerLngRange() { + new LatLng(0, 180); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(INVALID_LNG_MESSAGE); + new LatLng(0, -181); + } +} + diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngValueTest.java similarity index 55% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngValueTest.java index 7af82c29901d..583f89ef6cfe 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ListValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LatLngValueTest.java @@ -14,57 +14,39 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import com.google.common.collect.ImmutableList; - import org.junit.Test; -import java.util.List; - -public class ListValueTest { +public class LatLngValueTest { - private static final List> CONTENT = - ImmutableList.of(NullValue.of(), StringValue.of("foo")); +private static final LatLng CONTENT = new LatLng(37.4, -122.1); @Test public void testToBuilder() throws Exception { - ListValue value = ListValue.of(CONTENT); + LatLngValue value = LatLngValue.of(CONTENT); assertEquals(value, value.toBuilder().build()); } @SuppressWarnings("deprecation") @Test public void testOf() throws Exception { - ListValue value = ListValue.of(CONTENT); + LatLngValue value = LatLngValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); - } - - @Test(expected = DatastoreException.class) - public void testIndexedCannotBeSpecified() { - ListValue.builder().indexed(false); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { - ListValue.Builder builder = ListValue.builder().set(CONTENT); - ListValue value = builder.meaning(1).build(); + LatLngValue.Builder builder = LatLngValue.builder(CONTENT); + LatLngValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertFalse(value.hasIndexed()); - - builder = ListValue.builder(); - for (Value v : CONTENT) { - builder.addValue(v); - } - assertEquals(CONTENT, builder.build().get()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ListValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ListValueTest.java new file mode 100644 index 000000000000..567f2778dc38 --- /dev/null +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ListValueTest.java @@ -0,0 +1,202 @@ +/* + * 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.cloud.datastore; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +public class ListValueTest { + + private static final List> CONTENT = + ImmutableList.of(NullValue.of(), StringValue.of("foo")); + private static final String STRING1 = "string1"; + private static final String STRING2 = "string2"; + private static final long LONG1 = 1L; + private static final long LONG2 = 2L; + private static final double DOUBLE1 = 1.0; + private static final double DOUBLE2 = 2.0; + private static final boolean BOOLEAN1 = true; + private static final boolean BOOLEAN2 = false; + private static final DateTime DATETIME1 = new DateTime(1); + private static final DateTime DATETIME2 = new DateTime(2); + private static final LatLng LATLNG1 = LatLng.of(DOUBLE1, DOUBLE2); + private static final LatLng LATLNG2 = LatLng.of(DOUBLE2, DOUBLE1); + private static final Key KEY1 = Key.builder("project", "kind", "name1").build(); + private static final Key KEY2 = Key.builder("project", "kind", "name2").build(); + private static final FullEntity ENTITY1 = FullEntity.builder(KEY1).build(); + private static final FullEntity ENTITY2 = FullEntity.builder(KEY2).build(); + private static final Blob BLOB1 = Blob.copyFrom(new byte[]{0xD, 0xE, 0xA, 0xD}); + private static final Blob BLOB2 = Blob.copyFrom(new byte[]{0xB, 0x0, 0x0, 0x0}); + + @Test + public void testToBuilder() throws Exception { + ListValue value = ListValue.of(CONTENT); + assertEquals(value, value.toBuilder().build()); + } + + @Test + public void testOf() throws Exception { + ListValue value = ListValue.of(CONTENT); + assertEquals(CONTENT, value.get()); + assertFalse(value.excludeFromIndexes()); + value = ListValue.of(Collections.>emptyList()); + assertEquals(Collections.>emptyList(), value.get()); + assertFalse(value.excludeFromIndexes()); + value = ListValue.of(STRING1); + assertEquals(ImmutableList.of(StringValue.of(STRING1)), value.get()); + value = ListValue.of(STRING1, STRING2); + assertEquals(ImmutableList.of(StringValue.of(STRING1), StringValue.of(STRING2)), value.get()); + value = ListValue.of(LONG1); + assertEquals(ImmutableList.of(LongValue.of(LONG1)), value.get()); + value = ListValue.of(LONG1, LONG2); + assertEquals(ImmutableList.of(LongValue.of(LONG1), LongValue.of(LONG2)), value.get()); + value = ListValue.of(DOUBLE1); + assertEquals(ImmutableList.of(DoubleValue.of(DOUBLE1)), value.get()); + value = ListValue.of(DOUBLE1, DOUBLE2); + assertEquals(ImmutableList.of(DoubleValue.of(DOUBLE1), DoubleValue.of(DOUBLE2)), value.get()); + value = ListValue.of(BOOLEAN1); + assertEquals(ImmutableList.of(BooleanValue.of(BOOLEAN1)), value.get()); + value = ListValue.of(BOOLEAN1, BOOLEAN2); + assertEquals(ImmutableList.of(BooleanValue.of(BOOLEAN1), BooleanValue.of(BOOLEAN2)), + value.get()); + value = ListValue.of(DATETIME1); + assertEquals(ImmutableList.of(DateTimeValue.of(DATETIME1)), value.get()); + value = ListValue.of(DATETIME1, DATETIME2); + assertEquals(ImmutableList.of(DateTimeValue.of(DATETIME1), DateTimeValue.of(DATETIME2)), + value.get()); + value = ListValue.of(LATLNG1); + assertEquals(ImmutableList.of(LatLngValue.of(LATLNG1)), value.get()); + value = ListValue.of(LATLNG1, LATLNG2); + assertEquals(ImmutableList.of(LatLngValue.of(LATLNG1), LatLngValue.of(LATLNG2)), value.get()); + value = ListValue.of(KEY1); + assertEquals(ImmutableList.of(KeyValue.of(KEY1)), value.get()); + value = ListValue.of(KEY1, KEY2); + assertEquals(ImmutableList.of(KeyValue.of(KEY1), KeyValue.of(KEY2)), value.get()); + value = ListValue.of(ENTITY1); + assertEquals(ImmutableList.of(EntityValue.of(ENTITY1)), value.get()); + value = ListValue.of(ENTITY1, ENTITY2); + assertEquals(ImmutableList.of(EntityValue.of(ENTITY1), EntityValue.of(ENTITY2)), value.get()); + value = ListValue.of(BLOB1); + assertEquals(ImmutableList.of(BlobValue.of(BLOB1)), value.get()); + value = ListValue.of(BLOB1, BLOB2); + assertEquals(ImmutableList.of(BlobValue.of(BLOB1), BlobValue.of(BLOB2)), value.get()); + } + + @SuppressWarnings("deprecation") + @Test + public void testBuilder() throws Exception { + ListValue.Builder builder = ListValue.builder().set(CONTENT); + ListValue value = builder.meaning(1).excludeFromIndexes(true).build(); + assertEquals(CONTENT, value.get()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); + + builder = ListValue.builder(); + for (Value v : CONTENT) { + builder.addValue(v); + } + assertEquals(CONTENT, builder.build().get()); + + builder = builder.set(Collections.>emptyList()); + assertEquals(Collections.>emptyList(), builder.build().get()); + + builder = builder.addValue(STRING1); + assertEquals(ImmutableList.of(StringValue.of(STRING1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(STRING1, STRING2); + assertEquals(ImmutableList.of(StringValue.of(STRING1), StringValue.of(STRING2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(LONG1); + assertEquals(ImmutableList.of(LongValue.of(LONG1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(LONG1, LONG2); + assertEquals(ImmutableList.of(LongValue.of(LONG1), LongValue.of(LONG2)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(DOUBLE1); + assertEquals(ImmutableList.of(DoubleValue.of(DOUBLE1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(DOUBLE1, DOUBLE2); + assertEquals(ImmutableList.of(DoubleValue.of(DOUBLE1), DoubleValue.of(DOUBLE2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(BOOLEAN1); + assertEquals(ImmutableList.of(BooleanValue.of(BOOLEAN1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(BOOLEAN1, BOOLEAN2); + assertEquals(ImmutableList.of(BooleanValue.of(BOOLEAN1), BooleanValue.of(BOOLEAN2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(DATETIME1); + assertEquals(ImmutableList.of(DateTimeValue.of(DATETIME1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(DATETIME1, DATETIME2); + assertEquals(ImmutableList.of(DateTimeValue.of(DATETIME1), DateTimeValue.of(DATETIME2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(LATLNG1); + assertEquals(ImmutableList.of(LatLngValue.of(LATLNG1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(LATLNG1, LATLNG2); + assertEquals(ImmutableList.of(LatLngValue.of(LATLNG1), LatLngValue.of(LATLNG2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(KEY1); + assertEquals(ImmutableList.of(KeyValue.of(KEY1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(KEY1, KEY2); + assertEquals(ImmutableList.of(KeyValue.of(KEY1), KeyValue.of(KEY2)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(ENTITY1); + assertEquals(ImmutableList.of(EntityValue.of(ENTITY1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(ENTITY1, ENTITY2); + assertEquals(ImmutableList.of(EntityValue.of(ENTITY1), EntityValue.of(ENTITY2)), + builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(BLOB1); + assertEquals(ImmutableList.of(BlobValue.of(BLOB1)), builder.build().get()); + builder = builder.set(Collections.>emptyList()); + + builder = builder.addValue(BLOB1, BLOB2); + assertEquals(ImmutableList.of(BlobValue.of(BLOB1), BlobValue.of(BLOB2)), builder.build().get()); + } +} diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LongValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LongValueTest.java similarity index 80% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LongValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LongValueTest.java index c4c899785d68..135f5fb8ac6a 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LongValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/LongValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,19 +37,16 @@ public void testToBuilder() throws Exception { public void testOf() throws Exception { LongValue value = LongValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { LongValue.Builder builder = LongValue.builder(CONTENT); - LongValue value = builder.meaning(1).indexed(false).build(); + LongValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/NullValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/NullValueTest.java similarity index 77% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/NullValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/NullValueTest.java index a42fdaf0229f..ffed6e69c4f5 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/NullValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/NullValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -31,24 +31,20 @@ public void testToBuilder() throws Exception { assertEquals(value, value.toBuilder().build()); } - @SuppressWarnings("deprecation") @Test public void testOf() throws Exception { NullValue value = NullValue.of(); assertNull(value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { NullValue.Builder builder = NullValue.builder(); - NullValue value = builder.meaning(1).indexed(false).build(); + NullValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertNull(value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/PathElementTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/PathElementTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/PathElementTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/PathElementTest.java index 393521ff08b9..269dddb51d71 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/PathElementTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/PathElementTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ProjectionEntityTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ProjectionEntityTest.java similarity index 98% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ProjectionEntityTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ProjectionEntityTest.java index 43eec2f02001..2b53e6efc04c 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ProjectionEntityTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ProjectionEntityTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/RawValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/RawValueTest.java similarity index 71% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/RawValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/RawValueTest.java index 4d63bc89bacb..1d603888b5d1 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/RawValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/RawValueTest.java @@ -14,19 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import com.google.api.services.datastore.DatastoreV1; - import org.junit.Test; public class RawValueTest { - private static final DatastoreV1.Value CONTENT = StringValue.of("hello").toPb(); + private static final com.google.datastore.v1beta3.Value CONTENT = StringValue.of("hello").toPb(); @Test public void testToBuilder() throws Exception { @@ -34,24 +32,20 @@ public void testToBuilder() throws Exception { assertEquals(value, value.toBuilder().build()); } - @SuppressWarnings("deprecation") @Test public void testOf() throws Exception { RawValue value = RawValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { RawValue.Builder builder = RawValue.builder(CONTENT); - RawValue value = builder.meaning(1).indexed(false).build(); + RawValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/SerializationTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/SerializationTest.java new file mode 100644 index 000000000000..ab771f0118a1 --- /dev/null +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/SerializationTest.java @@ -0,0 +1,131 @@ +/* + * 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.cloud.datastore; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.cloud.AuthCredentials; +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.Restorable; +import com.google.cloud.datastore.StructuredQuery.CompositeFilter; +import com.google.cloud.datastore.StructuredQuery.OrderBy; +import com.google.cloud.datastore.StructuredQuery.PropertyFilter; + +public class SerializationTest extends BaseSerializationTest { + + private static final IncompleteKey INCOMPLETE_KEY1 = + IncompleteKey.builder("ds", "k").ancestors(PathElement.of("p", 1)).build(); + private static final Key KEY1 = Key.builder("ds", "k", "n").build(); + private static final IncompleteKey INCOMPLETE_KEY2 = + IncompleteKey.builder(KEY1, "v").ancestors(PathElement.of("p", 1)).build(); + private static final Key KEY2 = Key.builder(KEY1, "v", 2).build(); + private static final DateTime DATE_TIME1 = DateTime.now(); + private static final LatLng LAT_LNG = new LatLng(37.422035, -122.084124); + private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world")); + private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1, 2}); + private static final Cursor CURSOR2 = Cursor.copyFrom(new byte[] {10}); + private static final Query GQL1 = + Query.gqlQueryBuilder("select * from kind1 where name = @name and age > @1") + .setBinding("name", "name1") + .addBinding(20) + .namespace("ns1") + .build(); + private static final Query GQL2 = + Query.gqlQueryBuilder( + Query.ResultType.ENTITY, "select * from kind1 where name = @name and age > @1") + .setBinding("name", "name1") + .addBinding(20) + .namespace("ns1") + .build(); + private static final Query QUERY1 = + Query.entityQueryBuilder().kind("kind1").build(); + private static final Query QUERY2 = Query.keyQueryBuilder() + .kind("k") + .filter(PropertyFilter.eq("p1", "hello")) + .build(); + private static final Query QUERY3 = + Query.projectionEntityQueryBuilder() + .kind("k") + .namespace("ns1") + .projection("p") + .limit(100) + .offset(5) + .startCursor(CURSOR1) + .endCursor(CURSOR2) + .filter(CompositeFilter.and(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v"))) + .addDistinctOn("p") + .addOrderBy(OrderBy.asc("p")) + .build(); + private static final KeyValue KEY_VALUE = KeyValue.of(KEY1); + private static final NullValue NULL_VALUE = NullValue.builder().excludeFromIndexes(true).build(); + private static final StringValue STRING_VALUE = StringValue.of("hello"); + private static final LongValue LONG_VALUE = LongValue.of(123); + private static final DoubleValue DOUBLE_VALUE = DoubleValue.of(12.34); + private static final BooleanValue BOOLEAN_VALUE = BooleanValue.of(true); + private static final DateTimeValue DATE_AND_TIME_VALUE = DateTimeValue.of(DateTime.now()); + private static final BlobValue BLOB_VALUE = BlobValue.of(BLOB1); + private static final RawValue RAW_VALUE = + RawValue.of(com.google.datastore.v1beta3.Value.newBuilder().setMeaning(18).build()); + private static final LatLngValue LAT_LNG_VALUE = LatLngValue.of(LAT_LNG); + private static final Entity ENTITY1 = Entity.builder(KEY1).build(); + private static final Entity ENTITY2 = + Entity.builder(KEY2).set("null", NullValue.of()).build(); + private static final Entity ENTITY3 = Entity.builder(KEY2) + .set("p1", StringValue.builder("hi1").meaning(10).build()) + .set("p2", StringValue.builder("hi2").meaning(11).excludeFromIndexes(true).build()) + .set("p3", LongValue.builder(100).excludeFromIndexes(true).meaning(100).build()) + .set("blob", BLOB1) + .build(); + private static final FullEntity EMBEDDED_ENTITY = Entity.builder(INCOMPLETE_KEY1) + .set("p1", STRING_VALUE) + .set("p2", LongValue.builder(100).excludeFromIndexes(true).meaning(100).build()) + .build(); + private static final EntityValue EMBEDDED_ENTITY_VALUE1 = EntityValue.of(ENTITY1); + private static final EntityValue EMBEDDED_ENTITY_VALUE2 = EntityValue.of(ENTITY2); + private static final EntityValue EMBEDDED_ENTITY_VALUE3 = EntityValue.of(EMBEDDED_ENTITY); + private static final ListValue LIST_VALUE = ListValue.builder() + .addValue(NULL_VALUE) + .addValue(STRING_VALUE) + .addValue(new NullValue()) + .build(); + private static final ProjectionEntity PROJECTION_ENTITY = ProjectionEntity.fromPb(ENTITY1.toPb()); + private static final DatastoreException DATASTORE_EXCEPTION = + new DatastoreException(42, "message", "reason"); + + @Override + protected java.io.Serializable[] serializableObjects() { + DatastoreOptions options = DatastoreOptions.builder() + .authCredentials(AuthCredentials.createForAppEngine()) + .projectId("ds1") + .build(); + DatastoreOptions otherOptions = options.toBuilder() + .namespace("ns1") + .authCredentials(null) + .build(); + return new java.io.Serializable[]{KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, + ENTITY2, ENTITY3, EMBEDDED_ENTITY, PROJECTION_ENTITY, DATE_TIME1, BLOB1, CURSOR1, GQL1, + GQL2, QUERY1, QUERY2, QUERY3, NULL_VALUE, KEY_VALUE, STRING_VALUE, EMBEDDED_ENTITY_VALUE1, + EMBEDDED_ENTITY_VALUE2, EMBEDDED_ENTITY_VALUE3, LIST_VALUE, LONG_VALUE, DOUBLE_VALUE, + BOOLEAN_VALUE, DATE_AND_TIME_VALUE, BLOB_VALUE, RAW_VALUE, LAT_LNG_VALUE, + DATASTORE_EXCEPTION, options, otherOptions}; + } + + @Override + protected Restorable[] restorableObjects() { + return null; + } +} diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StringValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StringValueTest.java similarity index 78% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StringValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StringValueTest.java index a2cacd6574aa..8dab36015515 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StringValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StringValueTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -32,24 +32,20 @@ public void testToBuilder() throws Exception { assertEquals(value, value.toBuilder().build()); } - @SuppressWarnings("deprecation") @Test public void testOf() throws Exception { StringValue value = StringValue.of(CONTENT); assertEquals(CONTENT, value.get()); - assertFalse(value.hasIndexed()); - assertFalse(value.hasMeaning()); + assertFalse(value.excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testBuilder() throws Exception { StringValue.Builder builder = StringValue.builder(CONTENT); - StringValue value = builder.meaning(1).indexed(false).build(); + StringValue value = builder.meaning(1).excludeFromIndexes(true).build(); assertEquals(CONTENT, value.get()); - assertTrue(value.hasMeaning()); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertFalse(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StructuredQueryTest.java similarity index 81% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StructuredQueryTest.java index 4b6589efd723..cc2ec78e06a1 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/StructuredQueryTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/StructuredQueryTest.java @@ -14,19 +14,18 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.google.cloud.datastore.Query.ResultType; +import com.google.cloud.datastore.StructuredQuery.CompositeFilter; +import com.google.cloud.datastore.StructuredQuery.Filter; +import com.google.cloud.datastore.StructuredQuery.OrderBy; +import com.google.cloud.datastore.StructuredQuery.PropertyFilter; import com.google.common.collect.ImmutableList; -import com.google.gcloud.datastore.Query.ResultType; -import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; -import com.google.gcloud.datastore.StructuredQuery.Filter; -import com.google.gcloud.datastore.StructuredQuery.OrderBy; -import com.google.gcloud.datastore.StructuredQuery.Projection; -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; import org.junit.Test; @@ -45,12 +44,12 @@ public class StructuredQueryTest { private static final OrderBy ORDER_BY_1 = OrderBy.asc("p2"); private static final OrderBy ORDER_BY_2 = OrderBy.desc("p3"); private static final List ORDER_BY = ImmutableList.of(ORDER_BY_1, ORDER_BY_2); - private static final Projection PROJECTION1 = Projection.property("p4"); - private static final Projection PROJECTION2 = Projection.property("p5"); - private static final List PROJECTION = ImmutableList.of(PROJECTION1, PROJECTION2); - private static final String GROUP_BY1 = "p6"; - private static final String GROUP_BY2 = "p7"; - private static final List GROUP_BY = ImmutableList.of(GROUP_BY1, GROUP_BY2); + private static final String PROJECTION1 = "p4"; + private static final String PROJECTION2 = "p5"; + private static final List PROJECTION = ImmutableList.of(PROJECTION1, PROJECTION2); + private static final String DISTINCT_ON1 = "p6"; + private static final String DISTINCT_ON2 = "p7"; + private static final List DISTINCT_ON = ImmutableList.of(DISTINCT_ON1, DISTINCT_ON2); private static final EntityQuery ENTITY_QUERY = Query.entityQueryBuilder() .namespace(NAMESPACE) .kind(KIND) @@ -82,30 +81,28 @@ public class StructuredQueryTest { .filter(FILTER) .orderBy(ORDER_BY_1, ORDER_BY_2) .projection(PROJECTION1, PROJECTION2) - .groupBy(GROUP_BY1, GROUP_BY2) + .distinctOn(DISTINCT_ON1, DISTINCT_ON2) .build(); @Test public void testEntityQueryBuilder() { compareBaseBuilderFields(ENTITY_QUERY); assertTrue(ENTITY_QUERY.projection().isEmpty()); - assertTrue(ENTITY_QUERY.groupBy().isEmpty()); + assertTrue(ENTITY_QUERY.distinctOn().isEmpty()); } @Test public void testKeyQueryBuilder() { compareBaseBuilderFields(KEY_QUERY); - assertEquals( - ImmutableList.of(Projection.property(StructuredQuery.KEY_PROPERTY_NAME)), - KEY_QUERY.projection()); - assertTrue(KEY_QUERY.groupBy().isEmpty()); + assertEquals(ImmutableList.of(StructuredQuery.KEY_PROPERTY_NAME), KEY_QUERY.projection()); + assertTrue(KEY_QUERY.distinctOn().isEmpty()); } @Test public void testProjectionEntityQueryBuilder() { compareBaseBuilderFields(PROJECTION_QUERY); assertEquals(PROJECTION, PROJECTION_QUERY.projection()); - assertEquals(GROUP_BY, PROJECTION_QUERY.groupBy()); + assertEquals(DISTINCT_ON, PROJECTION_QUERY.distinctOn()); } private void compareBaseBuilderFields(StructuredQuery query) { @@ -138,7 +135,7 @@ private void compareMergedQuery(StructuredQuery expected, StructuredQuery assertEquals(expected.filter(), actual.filter()); assertEquals(expected.orderBy(), actual.orderBy()); assertEquals(expected.projection(), actual.projection()); - assertEquals(expected.groupBy(), actual.groupBy()); + assertEquals(expected.distinctOn(), actual.distinctOn()); } @Test diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ValueTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ValueTest.java similarity index 63% rename from gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ValueTest.java rename to gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ValueTest.java index 891668990f66..bb039d1a1ac9 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/ValueTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/ValueTest.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.datastore; +package com.google.cloud.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; @@ -42,21 +41,24 @@ public class ValueTest { private static final NullValue NULL_VALUE = NullValue.of(); private static final StringValue STRING_VALUE = StringValue.of("hello"); private static final RawValue RAW_VALUE = RawValue.of(STRING_VALUE.toPb()); + private static final LatLngValue LAT_LNG_VALUE = + LatLngValue.of(new LatLng(37.422035, -122.084124)); private static final ImmutableMap TYPES = ImmutableMap.builder() - .put(ValueType.NULL, new Object[] {NullValue.class, NULL_VALUE.get()}) - .put(ValueType.KEY, new Object[] {KeyValue.class, KEY}) - .put(ValueType.BLOB, new Object[] {BlobValue.class, BLOB}) - .put(ValueType.BOOLEAN, new Object[] {BooleanValue.class, Boolean.TRUE}) - .put(ValueType.DATE_TIME, new Object[] {DateTimeValue.class, DATE_TIME}) - .put(ValueType.DOUBLE, new Object[] {DoubleValue.class, 1.25D}) - .put(ValueType.ENTITY, new Object[] {EntityValue.class, ENTITY}) - .put(ValueType.LIST, - new Object[] {ListValue.class, ImmutableList.of(NULL_VALUE, STRING_VALUE, RAW_VALUE)}) - .put(ValueType.LONG, new Object[] {LongValue.class, 123L}) - .put(ValueType.RAW_VALUE, new Object[] {RawValue.class, RAW_VALUE.get()}) - .put(ValueType.STRING, new Object[] {StringValue.class, STRING_VALUE.get()}) - .build(); + .put(ValueType.NULL, new Object[] {NullValue.class, NULL_VALUE.get()}) + .put(ValueType.KEY, new Object[] {KeyValue.class, KEY}) + .put(ValueType.BLOB, new Object[] {BlobValue.class, BLOB}) + .put(ValueType.BOOLEAN, new Object[] {BooleanValue.class, Boolean.TRUE}) + .put(ValueType.DATE_TIME, new Object[] {DateTimeValue.class, DATE_TIME}) + .put(ValueType.DOUBLE, new Object[] {DoubleValue.class, 1.25D}) + .put(ValueType.ENTITY, new Object[] {EntityValue.class, ENTITY}) + .put(ValueType.LIST, new Object[] { + ListValue.class, ImmutableList.of(NULL_VALUE, STRING_VALUE, RAW_VALUE)}) + .put(ValueType.LONG, new Object[] {LongValue.class, 123L}) + .put(ValueType.RAW_VALUE, new Object[] {RawValue.class, RAW_VALUE.get()}) + .put(ValueType.LAT_LNG, new Object[] {LatLngValue.class, LAT_LNG_VALUE.get()}) + .put(ValueType.STRING, new Object[] {StringValue.class, STRING_VALUE.get()}) + .build(); private ImmutableMap> typeToValue; @@ -119,67 +121,21 @@ public void testType() throws Exception { } @Test - public void testHasIndexed() throws Exception { + public void testExcludeFromIndexes() throws Exception { for (Map.Entry> entry : typeToValue.entrySet()) { - ValueType valueType = entry.getKey(); - Boolean indexed = entry.getValue().hasIndexed(); - switch (valueType) { - case ENTITY: - assertTrue(indexed); - break; - default: - assertFalse(indexed); - break; - } + assertFalse(entry.getValue().excludeFromIndexes()); } - TestBuilder builder = new TestBuilder(); - assertFalse(builder.build().hasIndexed()); - assertTrue(builder.indexed(false).build().hasIndexed()); - assertTrue(builder.indexed(true).build().hasIndexed()); - } - - @Test - public void testIndexed() throws Exception { - for (Map.Entry> entry : typeToValue.entrySet()) { - ValueType valueType = entry.getKey(); - Boolean indexed = entry.getValue().indexed(); - switch (valueType) { - case ENTITY: - assertFalse(indexed); - break; - default: - assertNull(indexed); - break; - } - } - - TestBuilder builder = new TestBuilder(); - assertNull(builder.build().indexed()); - assertFalse(builder.indexed(false).build().indexed()); - assertTrue(builder.indexed(true).build().indexed()); - } - - @SuppressWarnings("deprecation") - @Test - public void testHasMeaning() throws Exception { - for (Value value : typeToValue.values()) { - assertFalse(value.hasMeaning()); - } - - TestBuilder builder = new TestBuilder(); - assertTrue(builder.meaning(10).build().hasMeaning()); + assertFalse(builder.build().excludeFromIndexes()); + assertTrue(builder.excludeFromIndexes(true).build().excludeFromIndexes()); + assertFalse(builder.excludeFromIndexes(false).build().excludeFromIndexes()); } @SuppressWarnings("deprecation") @Test public void testMeaning() throws Exception { - for (Value value : typeToValue.values()) { - assertNull(value.meaning()); - } - TestBuilder builder = new TestBuilder(); - assertEquals(Integer.valueOf(10), builder.meaning(10).build().meaning()); + assertEquals(10, builder.meaning(10).build().meaning()); } @Test @@ -201,12 +157,11 @@ public void testToBuilder() throws Exception { Set content = Collections.singleton("bla"); @SuppressWarnings("rawtypes") ValueBuilder builder = new TestBuilder(); - builder.meaning(1).set(content).indexed(true); + builder.meaning(1).set(content).excludeFromIndexes(true); Value value = builder.build(); builder = value.toBuilder(); - assertEquals(Integer.valueOf(1), value.meaning()); - assertTrue(value.hasIndexed()); - assertTrue(value.indexed()); + assertEquals(1, value.meaning()); + assertTrue(value.excludeFromIndexes()); assertEquals(ValueType.LIST, value.type()); assertEquals(content, value.get()); assertEquals(value, builder.build()); diff --git a/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/testing/LocalDatastoreHelperTest.java b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/testing/LocalDatastoreHelperTest.java new file mode 100644 index 000000000000..73844d2e19b3 --- /dev/null +++ b/gcloud-java-datastore/src/test/java/com/google/cloud/datastore/testing/LocalDatastoreHelperTest.java @@ -0,0 +1,53 @@ +/* + * 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.cloud.datastore.testing; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.AuthCredentials; +import com.google.cloud.datastore.DatastoreOptions; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class LocalDatastoreHelperTest { + + private static final double TOLERANCE = 0.00001; + private static final String PROJECT_ID_PREFIX = "test-project-"; + + @Test + public void testCreate() { + LocalDatastoreHelper helper = LocalDatastoreHelper.create(0.75); + assertTrue(Math.abs(0.75 - helper.consistency()) < TOLERANCE); + assertTrue(helper.projectId().startsWith(PROJECT_ID_PREFIX)); + helper = LocalDatastoreHelper.create(); + assertTrue(Math.abs(0.9 - helper.consistency()) < TOLERANCE); + assertTrue(helper.projectId().startsWith(PROJECT_ID_PREFIX)); + } + + @Test + public void testOptions() { + LocalDatastoreHelper helper = LocalDatastoreHelper.create(); + DatastoreOptions options = helper.options(); + assertTrue(options.projectId().startsWith(PROJECT_ID_PREFIX)); + assertTrue(options.host().startsWith("localhost:")); + assertSame(AuthCredentials.noAuth(), options.authCredentials()); + } +} diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelperTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelperTest.java deleted file mode 100644 index 5d761a713506..000000000000 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/LocalGcdHelperTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.datastore; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.google.gcloud.datastore.testing.LocalGcdHelper; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.IOException; -import java.net.ServerSocket; - -@RunWith(JUnit4.class) -public class LocalGcdHelperTest { - - private static final String PROJECT_ID = LocalGcdHelper.DEFAULT_PROJECT_ID; - private static final int PORT = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); - - @Test - public void testFindAvailablePort() { - int chosenPort = LocalGcdHelper.findAvailablePort(LocalGcdHelper.DEFAULT_PORT); - try (ServerSocket tempSocket = new ServerSocket(chosenPort)) { - // success - } catch (IOException e) { - if (chosenPort != LocalGcdHelper.DEFAULT_PORT) { - fail("Chosen port not free, even though LocalGcdHelper claimed it was."); - } - } - } - - @Test - public void testSendQuitRequest() throws IOException, InterruptedException { - LocalGcdHelper gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT, 0.75); - assertTrue(LocalGcdHelper.sendQuitRequest(PORT)); - long timeoutMillis = 30000; - long startTime = System.currentTimeMillis(); - boolean datastoreActive = LocalGcdHelper.isActive(PROJECT_ID, PORT); - while (datastoreActive && System.currentTimeMillis() - startTime < timeoutMillis) { - datastoreActive = LocalGcdHelper.isActive(PROJECT_ID, PORT); - } - assertFalse(datastoreActive); - assertFalse(LocalGcdHelper.sendQuitRequest(PORT)); - gcdHelper.stop(); - } - - @Test - public void testStartStop() throws IOException, InterruptedException { - LocalGcdHelper gcdHelper = LocalGcdHelper.start(PROJECT_ID, PORT, 0.75); - assertFalse(LocalGcdHelper.isActive("wrong-project-id", PORT)); - assertTrue(LocalGcdHelper.isActive(PROJECT_ID, PORT)); - gcdHelper.stop(); - assertFalse(LocalGcdHelper.isActive(PROJECT_ID, PORT)); - } -} diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java deleted file mode 100644 index 3976be2cc383..000000000000 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.datastore; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; - -import com.google.api.services.datastore.DatastoreV1; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; -import com.google.gcloud.AuthCredentials; -import com.google.gcloud.RetryParams; -import com.google.gcloud.datastore.StructuredQuery.CompositeFilter; -import com.google.gcloud.datastore.StructuredQuery.OrderBy; -import com.google.gcloud.datastore.StructuredQuery.Projection; -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter; - -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -public class SerializationTest { - - private static final IncompleteKey INCOMPLETE_KEY1 = - IncompleteKey.builder("ds", "k").ancestors(PathElement.of("p", 1)).build(); - private static final Key KEY1 = Key.builder("ds", "k", "n").build(); - private static final IncompleteKey INCOMPLETE_KEY2 = - IncompleteKey.builder(KEY1, "v").ancestors(PathElement.of("p", 1)).build(); - private static final Key KEY2 = Key.builder(KEY1, "v", 2).build(); - private static final DateTime DATE_TIME1 = DateTime.now(); - private static final Blob BLOB1 = Blob.copyFrom(UTF_8.encode("hello world")); - private static final Cursor CURSOR1 = Cursor.copyFrom(new byte[] {1, 2}); - private static final Cursor CURSOR2 = Cursor.copyFrom(new byte[] {10}); - private static final Query GQL1 = - Query.gqlQueryBuilder("select * from kind1 where name = @name and age > @1") - .setBinding("name", "name1") - .addBinding(20) - .namespace("ns1") - .build(); - private static final Query GQL2 = - Query.gqlQueryBuilder( - Query.ResultType.ENTITY, "select * from kind1 where name = @name and age > @1") - .setBinding("name", "name1") - .addBinding(20) - .namespace("ns1") - .build(); - private static final Query QUERY1 = - Query.entityQueryBuilder().kind("kind1").build(); - private static final Query QUERY2 = Query.keyQueryBuilder() - .kind("k") - .filter(PropertyFilter.eq("p1", "hello")) - .build(); - private static final Query QUERY3 = Query.projectionEntityQueryBuilder() - .kind("k") - .namespace("ns1") - .projection(Projection.property("p")) - .limit(100) - .offset(5) - .startCursor(CURSOR1) - .endCursor(CURSOR2) - .filter(CompositeFilter.and(PropertyFilter.gt("p1", 10), PropertyFilter.eq("a", "v"))) - .addGroupBy("p") - .addOrderBy(OrderBy.asc("p")) - .build(); - private static final KeyValue KEY_VALUE = KeyValue.of(KEY1); - private static final NullValue NULL_VALUE = NullValue.builder().indexed(true).build(); - private static final StringValue STRING_VALUE = StringValue.of("hello"); - private static final LongValue LONG_VALUE = LongValue.of(123); - private static final DoubleValue DOUBLE_VALUE = DoubleValue.of(12.34); - private static final BooleanValue BOOLEAN_VALUE = BooleanValue.of(true); - private static final DateTimeValue DATE_AND_TIME_VALUE = DateTimeValue.of(DateTime.now()); - private static final BlobValue BLOB_VALUE = BlobValue.of(BLOB1); - private static final RawValue RAW_VALUE = RawValue.of( - DatastoreV1.Value.newBuilder().setBlobKeyValue("blob-key").setMeaning(18).build()); - private static final Entity ENTITY1 = Entity.builder(KEY1).build(); - private static final Entity ENTITY2 = - Entity.builder(KEY2).set("null", NullValue.of()).build(); - private static final Entity ENTITY3 = Entity.builder(KEY2) - .set("p1", StringValue.builder("hi1").meaning(10).build()) - .set("p2", StringValue.builder("hi2").meaning(11).indexed(false).build()) - .set("p3", LongValue.builder(100).indexed(false).meaning(100).build()) - .set("blob", BLOB1) - .build(); - private static final FullEntity EMBEDDED_ENTITY = Entity.builder(INCOMPLETE_KEY1) - .set("p1", STRING_VALUE) - .set("p2", LongValue.builder(100).indexed(false).meaning(100).build()) - .build(); - private static final EntityValue EMBEDDED_ENTITY_VALUE1 = EntityValue.of(ENTITY1); - private static final EntityValue EMBEDDED_ENTITY_VALUE2 = EntityValue.of(ENTITY2); - private static final EntityValue EMBEDDED_ENTITY_VALUE3 = EntityValue.of(EMBEDDED_ENTITY); - private static final ListValue LIST_VALUE = ListValue.builder() - .addValue(NULL_VALUE) - .addValue(STRING_VALUE) - .addValue(new NullValue()) - .build(); - private static final ProjectionEntity PROJECTION_ENTITY = ProjectionEntity.fromPb(ENTITY1.toPb()); - - @SuppressWarnings("rawtypes") - private static final Multimap TYPE_TO_VALUES = - ImmutableMultimap.builder() - .put(ValueType.NULL, NULL_VALUE) - .put(ValueType.KEY, KEY_VALUE) - .put(ValueType.STRING, STRING_VALUE) - .putAll(ValueType.ENTITY, EMBEDDED_ENTITY_VALUE1, EMBEDDED_ENTITY_VALUE2, - EMBEDDED_ENTITY_VALUE3) - .put(ValueType.LIST, LIST_VALUE) - .put(ValueType.LONG, LONG_VALUE) - .put(ValueType.DOUBLE, DOUBLE_VALUE) - .put(ValueType.BOOLEAN, BOOLEAN_VALUE) - .put(ValueType.DATE_TIME, DATE_AND_TIME_VALUE) - .put(ValueType.BLOB, BLOB_VALUE) - .put(ValueType.RAW_VALUE, RAW_VALUE) - .build(); - - @Test - public void testServiceOptions() throws Exception { - DatastoreOptions options = DatastoreOptions.builder() - .authCredentials(AuthCredentials.createForAppEngine()) - .normalizeDataset(false) - .projectId("ds1") - .build(); - DatastoreOptions serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - - options = options.toBuilder() - .namespace("ns1") - .retryParams(RetryParams.defaultInstance()) - .authCredentials(null) - .force(true) - .build(); - serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - } - - @Test - public void testValues() throws Exception { - for (ValueType valueType : ValueType.values()) { - for (Value value : TYPE_TO_VALUES.get(valueType)) { - Value copy = serializeAndDeserialize(value); - assertEquals(value, value); - assertEquals(value, copy); - assertNotSame(value, copy); - assertEquals(copy, copy); - assertEquals(value.get(), copy.get()); - } - } - } - - @Test - public void testTypes() throws Exception { - Serializable[] types = { KEY1, KEY2, INCOMPLETE_KEY1, INCOMPLETE_KEY2, ENTITY1, ENTITY2, - ENTITY3, EMBEDDED_ENTITY, PROJECTION_ENTITY, DATE_TIME1, BLOB1, CURSOR1, GQL1, GQL2, - QUERY1, QUERY2, QUERY3}; - for (Serializable obj : types) { - Object copy = serializeAndDeserialize(obj); - assertEquals(obj, obj); - assertEquals(obj, copy); - assertNotSame(obj, copy); - assertEquals(copy, copy); - } - } - - private T serializeAndDeserialize(T obj) - throws IOException, ClassNotFoundException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { - output.writeObject(obj); - } - try (ObjectInputStream input = - new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { - @SuppressWarnings("unchecked") - T result = (T) input.readObject(); - return result; - } - } -} diff --git a/gcloud-java-dns/README.md b/gcloud-java-dns/README.md new file mode 100644 index 000000000000..994ab5615598 --- /dev/null +++ b/gcloud-java-dns/README.md @@ -0,0 +1,387 @@ +Google Cloud Java Client for DNS +================================ + +Java idiomatic client for [Google Cloud DNS] (https://cloud.google.com/dns/). + +[![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) +[![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-dns.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-dns.svg) +[![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) +[![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) + +- [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/dns/package-summary.html) + +> Note: This client is a work-in-progress, and may occasionally +> make backwards-incompatible changes. + +Quickstart +---------- +If you are using Maven, add this to your pom.xml file +```xml + + com.google.cloud + gcloud-java-dns + 0.2.0 + +``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.cloud:gcloud-java-dns:0.2.0' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.cloud" % "gcloud-java-dns" % "0.2.0" +``` + +Example Application +------------------- + +[`DnsExample`](../gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java) +is a simple command line interface that provides some of Google Cloud DNS's functionality. Read +more about using the application on the +[`DnsExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/dns/DnsExample.html). + +Authentication +-------------- + +See the [Authentication](https://github.com/GoogleCloudPlatform/gcloud-java#authentication) section +in the base directory's README. + +About Google Cloud DNS +-------------------------- + +[Google Cloud DNS][cloud-dns] is a scalable, reliable and managed authoritative Domain Name System +(DNS) service running on the same infrastructure as Google. It has low latency, high availability +and is a cost-effective way to make your applications and services available to your users. + +See the [Google Cloud DNS docs][dns-activate] for more details on how to activate +Cloud DNS for your project. + +See the [``gcloud-java-dns`` API documentation][dns-api] to learn how to interact +with the Cloud DNS using this client Library. + +Getting Started +--------------- +#### Prerequisites +For this tutorial, you will need a [Google Developers Console](https://console.developers.google.com/) +project with the DNS API enabled. You will need to [enable billing](https://support.google.com/cloud/answer/6158867?hl=en) +to use Google Cloud DNS. [Follow these instructions](https://cloud.google.com/docs/authentication#preparation) +to get your project set up. 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` and `gcloud config set project [YOUR PROJECT ID]`. + +#### Installation and setup +You'll need to obtain the `gcloud-java-dns` library. See the [Quickstart](#quickstart) section to +add `gcloud-java-dns` as a dependency in your code. + +#### Creating an authorized service object +To make authenticated requests to Google Cloud DNS, you must create a service object with +credentials. You can then make API calls by calling methods on the DNS 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.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; + +Dns dns = DnsOptions.defaultInstance().service(); +``` + +For other authentication options, see the [Authentication](https://github.com/GoogleCloudPlatform/gcloud-java#authentication) page. + +#### Managing Zones +Record sets in `gcloud-java-dns` are managed inside containers called "zones". `ZoneInfo` is a class +which encapsulates metadata that describe a zone in Google Cloud DNS. `Zone`, a subclass of `ZoneInfo`, adds service-related +functionality over `ZoneInfo`. + +*Important: Zone names must be unique to the project. If you choose a zone name that already +exists within your project, you'll get a helpful error message telling you to choose another name. In the code below, +replace "my-unique-zone" with a unique zone name. See more about naming rules [here](https://cloud.google.com/dns/api/v1/managedZones#name).* + +In this code snippet, we create a new zone to manage record sets for domain `someexampledomain.com.` + +*Important: The service may require that you verify ownership of the domain for which you are creating a zone. +Hence, we recommend that you do so beforehand. You can verify ownership of +a domain name [here](https://www.google.com/webmasters/verification/home). Note that Cloud DNS +requires fully qualified domain names which must end with a period.* + +Add the following imports at the top of your file: + +```java +import com.google.cloud.dns.Zone; +import com.google.cloud.dns.ZoneInfo; +``` + +Then add the following code to create a zone. + +```java +// Create a zone metadata object +String zoneName = "my-unique-zone"; // Change this zone name which is unique within your project +String domainName = "someexampledomain.com."; // Change this to a domain which you own +String description = "This is a gcloud-java-dns sample zone."; +ZoneInfo zoneInfo = ZoneInfo.of(zoneName, domainName, description); + +// Create zone in Google Cloud DNS +Zone zone = dns.create(zoneInfo); +System.out.printf("Zone was created and assigned ID %s.%n", zone.generatedId()); +``` + +You now have an empty zone hosted in Google Cloud DNS which is ready to be populated with +record sets for domain name `someexampledomain.com.` Upon creating the zone, the cloud service +assigned a set of DNS servers to host records for this zone and +created the required SOA and NS records for the domain. The following snippet prints the list of servers +assigned to the zone created above. First, import + +```java +import java.util.List; +``` + +and then add + +```java +// Print assigned name servers +List nameServers = zone.nameServers(); +for(String nameServer : nameServers) { + System.out.println(nameServer); +} +``` + +You can now instruct your domain registrar to [update your domain name servers] (https://cloud.google.com/dns/update-name-servers). +As soon as this happens and the change propagates through cached values in DNS resolvers, +all the DNS queries will be directed to and answered by the Google Cloud DNS service. + +#### Creating Record Sets +Now that we have a zone, we can add some record sets. The record sets held within zones are +modified by "change requests". In this example, we create and apply a change request to +our zone that creates a record set of type A and points URL www.someexampledomain.com to +IP address 12.13.14.15. Start by adding + +```java +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.RecordSet; + +import java.util.concurrent.TimeUnit; +``` + +and proceed with: + +```java +// Prepare a www.someexampledomain.com. type A record set with ttl of 24 hours +String ip = "12.13.14.15"; +RecordSet toCreate = RecordSet.builder("www." + zone.dnsName(), RecordSet.Type.A) + .ttl(24, TimeUnit.HOURS) + .addRecord(ip) + .build(); + +// Make a change +ChangeRequestInfo changeRequest = ChangeRequestInfo.builder().add(toCreate).build(); + +// Build and apply the change request to our zone +changeRequest = zone.applyChangeRequest(changeRequest); +``` + +The `addRecord` method of `RecordSet.Builder` accepts records in the form of +strings. The format of the strings depends on the type of the record sets to be added. +More information on the supported record set types and record formats can be found [here](https://cloud.google.com/dns/what-is-cloud-dns#supported_record_types). + +If you already have a record set, Cloud DNS will return an error upon an attempt to create a duplicate of it. +You can modify the code above to create a record set or update it if it already exists by making the +following adjustment in your imports + +```java +import java.util.Iterator; +``` + +and in the code + +```java +// Make a change +ChangeRequestInfo.Builder changeBuilder = ChangeRequestInfo.builder().add(toCreate); + +// Verify the type A record does not exist yet. +// If it does exist, we will overwrite it with our prepared record. +Iterator recordSetIterator = zone.listRecordSets().iterateAll(); +while (recordSetIterator.hasNext()) { + RecordSet current = recordSetIterator.next(); + if (toCreate.name().equals(current.name()) && toCreate.type().equals(current.type())) { + changeBuilder.delete(current); + } +} + +// Build and apply the change request to our zone +ChangeRequestInfo changeRequest = changeBuilder.build(); +ChangeRequest pendingRequest = zone.applyChangeRequest(changeRequest); +``` +You can find more information about changes in the [Cloud DNS documentation] (https://cloud.google.com/dns/what-is-cloud-dns#cloud_dns_api_concepts). + +When the change request is applied, it is registered with the Cloud DNS service for processing. We +can wait for its completion as follows: + +```java +while (!pendingRequest.isDone()) { + try { + Thread.sleep(500L); + } catch (InterruptedException e) { + System.err.println("The thread was interrupted while waiting..."); + } +} +System.out.println("The change request has been applied."); +``` + +Change requests are applied atomically to all the assigned DNS servers at once. Note that when this +happens, it may still take a while for the change to be registered by the DNS cache resolvers. +See more on this topic [here](https://cloud.google.com/dns/monitoring). + +#### Listing Zones and Record Sets +Suppose that you have added more zones and record sets, and now you want to list them. +First, import the following (unless you have done so in the previous section): + +```java +import java.util.Iterator; +``` + +Then add the following code to list all your zones and record sets. + +```java +// List all your zones +Iterator zoneIterator = dns.listZones().iterateAll(); +int counter = 1; +while (zoneIterator.hasNext()) { + System.out.printf("#%d.: %s%n%n", counter, zoneIterator.next()); + counter++; +} + +// List the record sets in a particular zone +recordSetIterator = zone.listRecordSets().iterateAll(); +System.out.println(String.format("Record sets inside %s:", zone.name())); +while (recordSetIterator.hasNext()) { + System.out.println(recordSetIterator.next()); +} +``` + +You can also list the history of change requests that were applied to a zone. +First add: + +```java +import java.util.ChangeRequest; +``` + +and then: + +```java + +// List the change requests applied to a particular zone +Iterator changeIterator = zone.listChangeRequests().iterateAll(); +System.out.println(String.format("The history of changes in %s:", zone.name())); +while (changeIterator.hasNext()) { + System.out.println(changeIterator.next()); +} +``` + +#### Deleting Zones + +If you no longer want to host a zone in Cloud DNS, you can delete it. +First, you need to empty the zone by deleting all its records except for the default SOA and NS record sets. + +```java +// Make a change for deleting the record sets +changeBuilder = ChangeRequestInfo.builder(); +while (recordIterator.hasNext()) { + RecordSet current = recordIterator.next(); + // SOA and NS records cannot be deleted + if (!RecordSet.Type.SOA.equals(current.type()) && !RecordSet.Type.NS.equals(current.type())) { + changeBuilder.delete(current); + } +} + +// Build and apply the change request to our zone if it contains records to delete +ChangeRequestInfo changeRequest = changeBuilder.build(); +if (!changeRequest.deletions().isEmpty()) { + ChangeRequest pendingRequest = dns.applyChangeRequest(zoneName, changeRequest); + + // Wait for the change request to complete + while (!pendingRequest.isDone()) { + System.out.println("Waiting for change to complete. Going to sleep for 500ms..."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.err.println("The thread was interrupted while waiting for change request to be " + + "processed."); + } + } +} + +// Delete the zone +boolean result = dns.delete(zoneName); +if (result) { + System.out.println("Zone was deleted."); +} else { + System.out.println("Zone was not deleted because it does not exist."); +} +``` + +#### Complete Source Code + +We composed some of the aforementioned snippets into complete executable code samples. In +[CreateZones.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateZone.java) +we create a zone. In [CreateOrUpdateRecordSets.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateOrUpdateRecordSets.java) +we create a type A record set for a zone, or update an existing type A record set to a new IP address. We +demonstrate how to delete a zone in [DeleteZone.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/DeleteZone.java). +Finally, in [ManipulateZonesAndRecordSets.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/ManipulateZonesAndRecordSets.java) +we assemble all the code snippets together and create zone, create or update a record set, list zones, list record sets, list changes, and +delete a zone. The applications assume that they are running on Compute Engine or from your own desktop. To run any of these examples on App +Engine, simply move the code from the main method to your application's servlet class and change the +print statements to display on your webpage. + +Troubleshooting +--------------- + +To get help, follow the `gcloud-java` links in the `gcloud-*` [shared Troubleshooting document](https://github.com/GoogleCloudPlatform/gcloud-common/blob/master/troubleshooting/readme.md#troubleshooting). + +Java Versions +------------- + +Java 7 or above is required for using this client. + +Testing +------- + +This library has tools to help make tests for code using Cloud DNS. + +See [TESTING] to read more about testing. + +Versioning +---------- + +This library follows [Semantic Versioning] (http://semver.org/). + +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. + +Contributing +------------ + +Contributions to this library are always welcome and highly encouraged. + +See `gcloud-java`'s [CONTRIBUTING] documentation and the `gcloud-*` [shared documentation](https://github.com/GoogleCloudPlatform/gcloud-common/blob/master/contributing/readme.md#how-to-contribute-to-gcloud) for more information on how to get started. + +Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. See [Code of Conduct][code-of-conduct] for more information. + +License +------- + +Apache 2.0 - See [LICENSE] for more information. + + +[CONTRIBUTING]:https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/CONTRIBUTING.md +[code-of-conduct]:https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/CODE_OF_CONDUCT.md#contributor-code-of-conduct +[LICENSE]: https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/LICENSE +[TESTING]: https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/TESTING.md#testing-code-that-uses-storage +[cloud-platform]: https://cloud.google.com/ + +[cloud-dns]: https://cloud.google.com/dns/ +[dns-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/dns/package-summary.html +[dns-activate]:https://cloud.google.com/dns/getting-started#prerequisites diff --git a/gcloud-java-dns/pom.xml b/gcloud-java-dns/pom.xml new file mode 100644 index 000000000000..73e949dd5b1e --- /dev/null +++ b/gcloud-java-dns/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + gcloud-java-dns + jar + GCloud Java DNS + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-dns + + Java idiomatic client for Google Cloud DNS. + + + com.google.cloud + gcloud-java-pom + 0.2.1-SNAPSHOT + + + gcloud-java-dns + + + + ${project.groupId} + gcloud-java-core + ${project.version} + + + com.google.apis + google-api-services-dns + v1-rev7-1.21.0 + compile + + + com.google.guava + guava-jdk5 + + + com.google.api-client + google-api-client + + + + + ${project.groupId} + gcloud-java-core + ${project.version} + test-jar + test + + + junit + junit + 4.12 + test + + + org.easymock + easymock + 3.3 + test + + + diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequest.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequest.java new file mode 100644 index 000000000000..3538a5c411e9 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequest.java @@ -0,0 +1,231 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.Change; +import com.google.common.base.Function; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.List; +import java.util.Objects; + +/** + * An immutable class representing an atomic update to a collection of {@link RecordSet}s within a + * {@code Zone}. + * + * @see Google Cloud DNS documentation + */ +public class ChangeRequest extends ChangeRequestInfo { + + private static final long serialVersionUID = 5335667200595081449L; + private final DnsOptions options; + private final String zone; + private transient Dns dns; + + /** + * A builder for {@code ChangeRequest}s. + */ + public static class Builder extends ChangeRequestInfo.Builder { + + private final Dns dns; + private final String zone; + private final ChangeRequestInfo.BuilderImpl infoBuilder; + + private Builder(ChangeRequest cr) { + this.dns = cr.dns; + this.zone = cr.zone; + this.infoBuilder = new ChangeRequestInfo.BuilderImpl(cr); + } + + @Override + public Builder additions(List additions) { + infoBuilder.additions(additions); + return this; + } + + @Override + public Builder deletions(List deletions) { + infoBuilder.deletions(deletions); + return this; + } + + @Override + public Builder add(RecordSet recordSet) { + infoBuilder.add(recordSet); + return this; + } + + @Override + public Builder delete(RecordSet recordSet) { + infoBuilder.delete(recordSet); + return this; + } + + @Override + public Builder clearAdditions() { + infoBuilder.clearAdditions(); + return this; + } + + @Override + public Builder clearDeletions() { + infoBuilder.clearDeletions(); + return this; + } + + @Override + public Builder removeAddition(RecordSet recordSet) { + infoBuilder.removeAddition(recordSet); + return this; + } + + @Override + public Builder removeDeletion(RecordSet recordSet) { + infoBuilder.removeDeletion(recordSet); + return this; + } + + @Override + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); + return this; + } + + @Override + Builder startTimeMillis(long startTimeMillis) { + infoBuilder.startTimeMillis(startTimeMillis); + return this; + } + + @Override + Builder status(Status status) { + infoBuilder.status(status); + return this; + } + + @Override + public ChangeRequest build() { + return new ChangeRequest(dns, zone, infoBuilder); + } + } + + ChangeRequest(Dns dns, String zone, ChangeRequest.BuilderImpl infoBuilder) { + super(infoBuilder); + this.zone = checkNotNull(zone); + this.dns = checkNotNull(dns); + this.options = dns.options(); + } + + /** + * Returns the name of the {@link Zone} associated with this change request. + */ + public String zone() { + return this.zone; + } + + /** + * Returns the change request's {@code Dns} object used to issue requests. + */ + public Dns dns() { + return dns; + } + + /** + * Applies this change request to the zone identified by {@code zoneName}. + * + * @throws DnsException upon failure or if zone is not found + */ + public ChangeRequest applyTo(String zoneName, Dns.ChangeRequestOption... options) { + return dns.applyChangeRequest(zoneName, this, options); + } + + /** + * Retrieves the up-to-date information about the change request from Google Cloud DNS. Parameter + * {@code options} can be used to restrict the fields to be included in the updated object the + * same way as in {@link Dns#getChangeRequest(String, String, Dns.ChangeRequestOption...)}. If + * {@code options} are provided, any field other than generatedId which is not included in the + * {@code options} will be {@code null} regardless of whether they are initialized or not in + * {@code this} instance. + * + * @return an object with the updated information or {@code null} if it does not exist + * @throws DnsException upon failure of the API call or if the associated zone was not found + */ + public ChangeRequest reload(Dns.ChangeRequestOption... options) { + return dns.getChangeRequest(zone, generatedId(), options); + } + + /** + * Returns {@code true} if the change request has been completed. If the status is not {@link + * Status#DONE} already, the method makes an API call to Google Cloud DNS to update the change + * request first. + * + * @throws DnsException upon failure of the API call or if the associated zone was not found + */ + public boolean isDone() { + if (status() == Status.DONE) { + return true; + } + ChangeRequest updated = reload(Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS)); + return updated == null || updated.status() == Status.DONE; + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(ChangeRequest.class)) { + return false; + } + ChangeRequest other = (ChangeRequest) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options) + && Objects.equals(zone, other.zone); + } + + @Override + public final int hashCode() { + return Objects.hash(super.hashCode(), options, zone); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + this.dns = options.service(); + } + + static ChangeRequest fromPb(Dns dns, String zoneName, Change pb) { + ChangeRequestInfo info = ChangeRequestInfo.fromPb(pb); + return new ChangeRequest(dns, zoneName, new ChangeRequestInfo.BuilderImpl(info)); + } + + static Function fromPbFunction(final Dns dns, final String zoneName) { + return new Function() { + @Override + public ChangeRequest apply(Change pb) { + return ChangeRequest.fromPb(dns, zoneName, pb); + } + }; + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequestInfo.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequestInfo.java new file mode 100644 index 000000000000..8ed68765a163 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ChangeRequestInfo.java @@ -0,0 +1,360 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.Change; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.joda.time.DateTime; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * A class representing an atomic update to a collection of {@link RecordSet}s within a {@code + * Zone}. + * + * @see Google Cloud DNS documentation + */ +public class ChangeRequestInfo implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public ChangeRequestInfo apply(Change pb) { + return ChangeRequestInfo.fromPb(pb); + } + }; + private static final long serialVersionUID = -6029143477639439169L; + private final List additions; + private final List deletions; + private final String generatedId; + private final Long startTimeMillis; + private final ChangeRequestInfo.Status status; + + /** + * This enumerates the possible states of a change request. + * + * @see Google Cloud DNS + * documentation + */ + public enum Status { + PENDING, + DONE + } + + /** + * A builder for {@code ChangeRequestInfo}. + */ + public abstract static class Builder { + + /** + * Sets a collection of {@link RecordSet}s which are to be added to the zone upon executing this + * {@code ChangeRequestInfo}. + */ + public abstract Builder additions(List additions); + + /** + * Sets a collection of {@link RecordSet}s which are to be deleted from the zone upon executing + * this {@code ChangeRequestInfo}. + */ + public abstract Builder deletions(List deletions); + + /** + * Adds a {@link RecordSet} to be added to the zone upon executing this {@code + * ChangeRequestInfo}. + */ + public abstract Builder add(RecordSet recordSet); + + /** + * Adds a {@link RecordSet} to be deleted to the zone upon executing this + * {@code ChangeRequestInfo}. + */ + public abstract Builder delete(RecordSet recordSet); + + /** + * Clears the collection of {@link RecordSet}s which are to be added to the zone upon executing + * this {@code ChangeRequestInfo}. + */ + public abstract Builder clearAdditions(); + + /** + * Clears the collection of {@link RecordSet}s which are to be deleted from the zone upon + * executing this {@code ChangeRequestInfo}. + */ + public abstract Builder clearDeletions(); + + /** + * Removes a single {@link RecordSet} from the collection of records to be + * added to the zone upon executing this {@code ChangeRequestInfo}. + */ + public abstract Builder removeAddition(RecordSet recordSet); + + /** + * Removes a single {@link RecordSet} from the collection of records to be + * deleted from the zone upon executing this {@code ChangeRequestInfo}. + */ + public abstract Builder removeDeletion(RecordSet recordSet); + + /** + * Associates a service-generated id to this {@code ChangeRequestInfo}. + */ + abstract Builder generatedId(String generatedId); + + /** + * Sets the time when this change request was started by a server. + */ + abstract Builder startTimeMillis(long startTimeMillis); + + /** + * Sets the current status of this {@code ChangeRequest}. + */ + abstract Builder status(ChangeRequest.Status status); + + /** + * Creates a {@code ChangeRequestInfo} instance populated by the values associated with this + * builder. + */ + public abstract ChangeRequestInfo build(); + } + + static class BuilderImpl extends Builder { + private List additions; + private List deletions; + private String generatedId; + private Long startTimeMillis; + private ChangeRequestInfo.Status status; + + BuilderImpl() { + this.additions = new LinkedList<>(); + this.deletions = new LinkedList<>(); + } + + BuilderImpl(ChangeRequestInfo info) { + this.additions = Lists.newLinkedList(info.additions()); + this.deletions = Lists.newLinkedList(info.deletions()); + this.generatedId = info.generatedId; + this.startTimeMillis = info.startTimeMillis; + this.status = info.status; + } + + @Override + public Builder additions(List additions) { + this.additions = Lists.newLinkedList(checkNotNull(additions)); + return this; + } + + @Override + public Builder deletions(List deletions) { + this.deletions = Lists.newLinkedList(checkNotNull(deletions)); + return this; + } + + @Override + public Builder add(RecordSet recordSet) { + this.additions.add(checkNotNull(recordSet)); + return this; + } + + @Override + public Builder delete(RecordSet recordSet) { + this.deletions.add(checkNotNull(recordSet)); + return this; + } + + @Override + public Builder clearAdditions() { + this.additions.clear(); + return this; + } + + @Override + public Builder clearDeletions() { + this.deletions.clear(); + return this; + } + + @Override + public Builder removeAddition(RecordSet recordSet) { + this.additions.remove(recordSet); + return this; + } + + @Override + public Builder removeDeletion(RecordSet recordSet) { + this.deletions.remove(recordSet); + return this; + } + + @Override + public ChangeRequestInfo build() { + return new ChangeRequestInfo(this); + } + + @Override + Builder generatedId(String generatedId) { + this.generatedId = checkNotNull(generatedId); + return this; + } + + @Override + Builder startTimeMillis(long startTimeMillis) { + this.startTimeMillis = startTimeMillis; + return this; + } + + @Override + Builder status(ChangeRequestInfo.Status status) { + this.status = checkNotNull(status); + return this; + } + } + + ChangeRequestInfo(BuilderImpl builder) { + this.additions = ImmutableList.copyOf(builder.additions); + this.deletions = ImmutableList.copyOf(builder.deletions); + this.generatedId = builder.generatedId; + this.startTimeMillis = builder.startTimeMillis; + this.status = builder.status; + } + + /** + * Returns an empty builder for the {@code ChangeRequestInfo} class. + */ + public static Builder builder() { + return new BuilderImpl(); + } + + /** + * Creates a builder populated with values of this {@code ChangeRequestInfo}. + */ + public Builder toBuilder() { + return new BuilderImpl(this); + } + + /** + * Returns the list of {@link RecordSet}s to be added to the zone upon submitting this change + * request. + */ + public List additions() { + return additions; + } + + /** + * Returns the list of {@link RecordSet}s to be deleted from the zone upon submitting this change + * request. + */ + public List deletions() { + return deletions; + } + + /** + * Returns the service-generated id for this change request. + */ + public String generatedId() { + return generatedId; + } + + /** + * Returns the time when this change request was started by the server. + */ + public Long startTimeMillis() { + return startTimeMillis; + } + + /** + * Returns the status of this change request. If the change request has not been applied yet, the + * status is {@code PENDING}. + */ + public ChangeRequestInfo.Status status() { + return status; + } + + Change toPb() { + Change pb = new Change(); + // set id + if (generatedId() != null) { + pb.setId(generatedId()); + } + // set timestamp + if (startTimeMillis() != null) { + pb.setStartTime(ISODateTimeFormat.dateTime().withZoneUTC().print(startTimeMillis())); + } + // set status + if (status() != null) { + pb.setStatus(status().name().toLowerCase()); + } + // set a list of additions + pb.setAdditions(Lists.transform(additions(), RecordSet.TO_PB_FUNCTION)); + // set a list of deletions + pb.setDeletions(Lists.transform(deletions(), RecordSet.TO_PB_FUNCTION)); + return pb; + } + + static ChangeRequestInfo fromPb(Change pb) { + Builder builder = builder(); + if (pb.getId() != null) { + builder.generatedId(pb.getId()); + } + if (pb.getStartTime() != null) { + builder.startTimeMillis(DateTime.parse(pb.getStartTime()).getMillis()); + } + if (pb.getStatus() != null) { + // we are assuming that status indicated in pb is a lower case version of the enum name + builder.status(ChangeRequest.Status.valueOf(pb.getStatus().toUpperCase())); + } + if (pb.getDeletions() != null) { + builder.deletions(Lists.transform(pb.getDeletions(), RecordSet.FROM_PB_FUNCTION)); + } + if (pb.getAdditions() != null) { + builder.additions(Lists.transform(pb.getAdditions(), RecordSet.FROM_PB_FUNCTION)); + } + return builder.build(); + } + + @Override + public boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ChangeRequestInfo.class) + && toPb().equals(((ChangeRequestInfo) obj).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(additions, deletions, generatedId, startTimeMillis, status); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("additions", additions) + .add("deletions", deletions) + .add("generatedId", generatedId) + .add("startTimeMillis", startTimeMillis) + .add("status", status) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/Dns.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Dns.java new file mode 100644 index 000000000000..2aa080fb63d3 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Dns.java @@ -0,0 +1,506 @@ +/* + * Copyright 2016 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.cloud.dns; + +import com.google.cloud.FieldSelector; +import com.google.cloud.FieldSelector.Helper; +import com.google.cloud.Page; +import com.google.cloud.Service; +import com.google.cloud.dns.spi.DnsRpc; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * An interface for the Google Cloud DNS service. + * + * @see Google Cloud DNS + */ +public interface Dns extends Service { + + /** + * The fields of a project. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#getProject(ProjectOption...)}. Project ID is always returned, even if not + * specified. + */ + enum ProjectField implements FieldSelector { + PROJECT_ID("id"), + PROJECT_NUMBER("number"), + QUOTA("quota"); + + static final List REQUIRED_FIELDS = ImmutableList.of(PROJECT_ID); + + private final String selector; + + ProjectField(String selector) { + this.selector = selector; + } + + @Override + public String selector() { + return selector; + } + } + + /** + * The fields of a zone. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#getZone(String, ZoneOption...)}. The name is always returned, even if not + * specified. + */ + enum ZoneField implements FieldSelector { + CREATION_TIME("creationTime"), + DESCRIPTION("description"), + DNS_NAME("dnsName"), + ZONE_ID("id"), + NAME("name"), + NAME_SERVER_SET("nameServerSet"), + NAME_SERVERS("nameServers"); + + static final List REQUIRED_FIELDS = ImmutableList.of(NAME); + + private final String selector; + + ZoneField(String selector) { + this.selector = selector; + } + + @Override + public String selector() { + return selector; + } + } + + /** + * The fields of a record set. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#listRecordSets(String, RecordSetListOption...)}. The name and type are always + * returned even if not selected. + */ + enum RecordSetField implements FieldSelector { + DNS_RECORDS("rrdatas"), + NAME("name"), + TTL("ttl"), + TYPE("type"); + + static final List REQUIRED_FIELDS = ImmutableList.of(NAME, TYPE); + + private final String selector; + + RecordSetField(String selector) { + this.selector = selector; + } + + @Override + public String selector() { + return selector; + } + } + + /** + * The fields of a change request. + * + *

These values can be used to specify the fields to include in a partial response when calling + * {@link Dns#applyChangeRequest(String, ChangeRequestInfo, ChangeRequestOption...)} The ID is + * always returned even if not selected. + */ + enum ChangeRequestField implements FieldSelector { + ID("id"), + START_TIME("startTime"), + STATUS("status"), + ADDITIONS("additions"), + DELETIONS("deletions"); + + static final List REQUIRED_FIELDS = ImmutableList.of(ID); + + private final String selector; + + ChangeRequestField(String selector) { + this.selector = selector; + } + + @Override + public String selector() { + return selector; + } + } + + /** + * The sorting order for listing. + */ + enum SortingOrder { + DESCENDING, ASCENDING; + + public String selector() { + return name().toLowerCase(); + } + } + + /** + * Class for specifying record set listing options. + */ + class RecordSetListOption extends Option { + + private static final long serialVersionUID = 1009627025381096098L; + + RecordSetListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the record set's fields to be returned by the RPC call. + * + *

If this option is not provided all record fields are returned. {@code + * RecordSetField.fields} can be used to specify only the fields of interest. The name of the + * record set in always returned, even if not specified. {@link RecordSetField} provides a list + * of fields that can be used. + */ + public static RecordSetListOption fields(RecordSetField... fields) { + return new RecordSetListOption(DnsRpc.Option.FIELDS, + Helper.listSelector("rrsets", RecordSetField.REQUIRED_FIELDS, fields)); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static RecordSetListOption pageToken(String pageToken) { + return new RecordSetListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * The maximum number of record sets to return per RPC. + * + *

The server can return fewer record sets than requested. When there are more results than + * the page size, the server will return a page token that can be used to fetch other results. + */ + public static RecordSetListOption pageSize(int pageSize) { + return new RecordSetListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + + /** + * Restricts the list to only record sets with this fully qualified domain name. + */ + public static RecordSetListOption dnsName(String dnsName) { + return new RecordSetListOption(DnsRpc.Option.NAME, dnsName); + } + + /** + * Restricts the list to return only record sets of this type. If present, {@link + * RecordSetListOption#dnsName(String)} must also be present. + */ + public static RecordSetListOption type(RecordSet.Type type) { + return new RecordSetListOption(DnsRpc.Option.DNS_TYPE, type.name()); + } + } + + /** + * Class for specifying zone field options. + */ + class ZoneOption extends Option { + + private static final long serialVersionUID = -8065564464895945037L; + + ZoneOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the zones's fields to be returned by the RPC call. + * + *

If this option is not provided all zone fields are returned. {@code ZoneOption.fields} can + * be used to specify only the fields of interest. Zone ID is always returned, even if not + * specified. {@link ZoneField} provides a list of fields that can be used. + */ + public static ZoneOption fields(ZoneField... fields) { + return new ZoneOption(DnsRpc.Option.FIELDS, + Helper.selector(ZoneField.REQUIRED_FIELDS, fields)); + } + } + + /** + * Class for specifying zone listing options. + */ + class ZoneListOption extends Option { + + private static final long serialVersionUID = -2830645032124504717L; + + ZoneListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify the zones's fields to be returned by the RPC call. + * + *

If this option is not provided all zone fields are returned. {@code ZoneOption.fields} can + * be used to specify only the fields of interest. Zone ID is always returned, even if not + * specified. {@link ZoneField} provides a list of fields that can be used. + */ + public static ZoneListOption fields(ZoneField... fields) { + return new ZoneListOption(DnsRpc.Option.FIELDS, + Helper.listSelector("managedZones", ZoneField.REQUIRED_FIELDS, fields)); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static ZoneListOption pageToken(String pageToken) { + return new ZoneListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * Restricts the list to only zone with this fully qualified domain name. + */ + public static ZoneListOption dnsName(String dnsName) { + return new ZoneListOption(DnsRpc.Option.DNS_NAME, dnsName); + } + + /** + * The maximum number of zones to return per RPC. + * + *

The server can return fewer zones than requested. When there are more results than the + * page size, the server will return a page token that can be used to fetch other results. + */ + public static ZoneListOption pageSize(int pageSize) { + return new ZoneListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + } + + /** + * Class for specifying project options. + */ + class ProjectOption extends Option { + + private static final long serialVersionUID = 6817937338218847748L; + + ProjectOption(DnsRpc.Option option, Object value) { + super(option, 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. {@code + * ProjectOption.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. + */ + public static ProjectOption fields(ProjectField... fields) { + return new ProjectOption(DnsRpc.Option.FIELDS, + Helper.selector(ProjectField.REQUIRED_FIELDS, fields)); + } + } + + /** + * Class for specifying change request field options. + */ + class ChangeRequestOption extends Option { + + private static final long serialVersionUID = 1067273695061077782L; + + ChangeRequestOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify which fields of {@link ChangeRequest} should be returned by the + * service. + * + *

If this option is not provided all change request fields are returned. {@code + * ChangeRequestOption.fields} can be used to specify only the fields of interest. The ID of the + * change request is always returned, even if not specified. {@link ChangeRequestField} provides + * a list of fields that can be used. + */ + public static ChangeRequestOption fields(ChangeRequestField... fields) { + return new ChangeRequestOption(DnsRpc.Option.FIELDS, + Helper.selector(ChangeRequestField.REQUIRED_FIELDS, fields)); + } + } + + /** + * Class for specifying change request listing options. + */ + class ChangeRequestListOption extends Option { + + private static final long serialVersionUID = -900209143895376089L; + + ChangeRequestListOption(DnsRpc.Option option, Object value) { + super(option, value); + } + + /** + * Returns an option to specify which fields of{@link ChangeRequest} should be returned by the + * service. + * + *

If this option is not provided all change request fields are returned. {@code + * ChangeRequestOption.fields} can be used to specify only the fields of interest. The ID of the + * change request is always returned, even if not specified. {@link ChangeRequestField} provides + * a list of fields that can be used. + */ + public static ChangeRequestListOption fields(ChangeRequestField... fields) { + return new ChangeRequestListOption(DnsRpc.Option.FIELDS, + Helper.listSelector("changes", ChangeRequestField.REQUIRED_FIELDS, fields)); + } + + /** + * Returns an option to specify a page token. + * + *

The page token (returned from a previous call to list) indicates from where listing should + * continue. + */ + public static ChangeRequestListOption pageToken(String pageToken) { + return new ChangeRequestListOption(DnsRpc.Option.PAGE_TOKEN, pageToken); + } + + /** + * The maximum number of change requests to return per RPC. + * + *

The server can return fewer change requests than requested. When there are more results + * than the page size, the server will return a page token that can be used to fetch other + * results. + */ + public static ChangeRequestListOption pageSize(int pageSize) { + return new ChangeRequestListOption(DnsRpc.Option.PAGE_SIZE, pageSize); + } + + /** + * Returns an option to specify whether the the change requests should be listed in ascending + * (most-recent last) or descending (most-recent first) order with respect to when the change + * request was accepted by the server. If this option is not provided, the listing order is + * undefined. + */ + public static ChangeRequestListOption sortOrder(SortingOrder order) { + return new ChangeRequestListOption(DnsRpc.Option.SORTING_ORDER, order.selector()); + } + } + + /** + * Creates a new zone. + * + *

Returns {@link Zone} object representing the new zone's information. In addition to the + * name, dns name and description (supplied by the user within the {@code zoneInfo} parameter), + * the returned object can include the following read-only fields supplied by the server: creation + * time, id, and list of name servers. The returned fields can be optionally restricted by + * specifying {@link ZoneOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * create + */ + Zone create(ZoneInfo zoneInfo, ZoneOption... options); + + /** + * Returns the zone by the specified zone name. Returns {@code null} if the zone is not found. The + * returned fields can be optionally restricted by specifying {@link ZoneOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * get + */ + Zone getZone(String zoneName, ZoneOption... options); + + /** + * Lists the zones inside the project. + * + *

This method returns zones in an unspecified order. New zones do not necessarily appear at + * the end of the list. Use {@link ZoneListOption} to restrict the listing to a domain name, set + * page size, and set page token. + * + * @return a page of zones + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * list + */ + Page listZones(ZoneListOption... options); + + /** + * Deletes an existing zone identified by name. Returns {@code true} if the zone was successfully + * deleted and {@code false} otherwise. + * + * @return {@code true} if zone was found and deleted and {@code false} otherwise + * @throws DnsException upon failure + * @see Cloud DNS Managed Zones: + * delete + */ + boolean delete(String zoneName); // delete does not admit any options + + /** + * Lists the record sets in the zone identified by name. + * + *

The fields to be returned, page size and page tokens can be specified using {@link + * RecordSetListOption}s. + * + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS + * ResourceRecordSets: list + */ + Page listRecordSets(String zoneName, RecordSetListOption... options); + + /** + * Retrieves the information about the current project. The returned fields can be optionally + * restricted by specifying {@link ProjectOption}s. + * + * @throws DnsException upon failure + * @see Cloud DNS Projects: get + */ + ProjectInfo getProject(ProjectOption... fields); + + /** + * Submits a change request for the specified zone. The returned object contains the following + * read-only fields supplied by the server: id, start time and status. time, id, and list of name + * servers. The fields to be returned can be selected by {@link ChangeRequestOption}s. + * + * @return the new {@link ChangeRequest} + * @throws DnsException upon failure or if zone is not found + * @see Cloud DNS Changes: create + */ + ChangeRequest applyChangeRequest(String zoneName, ChangeRequestInfo changeRequest, + ChangeRequestOption... options); + + /** + * Retrieves updated information about a change request previously submitted for a zone identified + * by ID. Returns {@code null} if the request cannot be found and throws an exception if the zone + * does not exist. The fields to be returned using can be specified using {@link + * ChangeRequestOption}s. + * + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS Chages: get + */ + ChangeRequest getChangeRequest(String zoneName, String changeRequestId, + ChangeRequestOption... options); + + /** + * Lists the change requests for the zone identified by name that were submitted to the service. + * + *

The sorting order for changes (based on when they were received by the server), fields to be + * returned, page size and page token can be specified using {@link ChangeRequestListOption}s. + * + * @return A page of change requests + * @throws DnsException upon failure or if the zone cannot be found + * @see Cloud DNS Chages: list + */ + Page listChangeRequests(String zoneName, ChangeRequestListOption... options); +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsException.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsException.java new file mode 100644 index 000000000000..f725984b6661 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsException.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 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.cloud.dns; + +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; +import com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.Set; + +/** + * DNS service exception. + */ +public class DnsException extends BaseServiceException { + + // see: https://cloud.google.com/dns/troubleshooting + private static final Set RETRYABLE_ERRORS = ImmutableSet.of( + new Error(429, null, true), + new Error(500, null, false), + new Error(502, null, false), + new Error(503, null, false), + new Error(null, "userRateLimitExceeded", true), + new Error(null, "rateLimitExceeded", true)); + private static final long serialVersionUID = 490302380416260252L; + + public DnsException(IOException exception, boolean idempotent) { + super(exception, idempotent); + } + + private DnsException(int code, String message) { + super(code, message, null, true); + } + + @Override + protected Set retryableErrors() { + return RETRYABLE_ERRORS; + } + + /** + * Translate RetryHelperException to the DnsException that caused the error. This method will + * always throw an exception. + * + * @throws DnsException when {@code ex} was caused by a {@code DnsException} + * @throws RetryInterruptedException when {@code ex} is a {@code RetryInterruptedException} + */ + static DnsException translateAndThrow(RetryHelperException ex) { + BaseServiceException.translateAndPropagateIfPossible(ex); + throw new DnsException(UNKNOWN_CODE, ex.getMessage()); + } +} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsFactory.java similarity index 50% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java rename to gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsFactory.java index 5924174ab138..38c4c68de596 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/OptionTest.java +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. + * Copyright 2016 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. @@ -14,25 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.dns; -import static org.junit.Assert.assertEquals; +import com.google.cloud.ServiceFactory; -import com.google.gcloud.storage.spi.StorageRpc; - -import org.junit.Test; - -public class OptionTest { - - @Test - public void testOption() { - Option option = new Option(StorageRpc.Option.DELIMITER, "/"); - assertEquals(StorageRpc.Option.DELIMITER, option.rpcOption()); - assertEquals("/", option.value()); - } - - @Test(expected = NullPointerException.class) - public void testIndexOutOfBoundsException() { - new Option(null, "/"); - } +/** + * An interface for Dns factories. + */ +public interface DnsFactory extends ServiceFactory { } diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsImpl.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsImpl.java new file mode 100644 index 000000000000..cdb311adc823 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsImpl.java @@ -0,0 +1,317 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.cloud.RetryHelper.runWithRetries; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.cloud.BaseService; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.RetryHelper; +import com.google.cloud.dns.spi.DnsRpc; +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 java.util.Map; +import java.util.concurrent.Callable; + +/** + * A default implementation of Dns. + */ +final class DnsImpl extends BaseService implements Dns { + + private final DnsRpc dnsRpc; + + private static class ZonePageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = 2158209410430566961L; + private final Map requestOptions; + private final DnsOptions serviceOptions; + + ZonePageFetcher(DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listZones(serviceOptions, requestOptions); + } + } + + private static class ChangeRequestPageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = 4473265130673029139L; + private final String zoneName; + private final Map requestOptions; + private final DnsOptions serviceOptions; + + ChangeRequestPageFetcher(String zoneName, DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.zoneName = zoneName; + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listChangeRequests(zoneName, serviceOptions, requestOptions); + } + } + + private static class DnsRecordPageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = -6039369212511530846L; + private final Map requestOptions; + private final DnsOptions serviceOptions; + private final String zoneName; + + DnsRecordPageFetcher(String zoneName, DnsOptions serviceOptions, String cursor, + Map optionMap) { + this.zoneName = zoneName; + this.requestOptions = + PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); + this.serviceOptions = serviceOptions; + } + + @Override + public Page nextPage() { + return listRecordSets(zoneName, serviceOptions, requestOptions); + } + } + + DnsImpl(DnsOptions options) { + super(options); + dnsRpc = options.rpc(); + } + + @Override + public Page listZones(ZoneListOption... options) { + return listZones(options(), optionMap(options)); + } + + private static Page listZones(final DnsOptions serviceOptions, + final Map optionsMap) { + // define transformation function + // this differs from the other list operations since zone is functional and requires dns service + Function pbToZoneFunction = new Function() { + @Override + public Zone apply(ManagedZone zonePb) { + return Zone.fromPb(serviceOptions.service(), zonePb); + } + }; + try { + // get a list of managed zones + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = + runWithRetries(new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listZones(optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into zone objects + Iterable zones = result.results() == null + ? ImmutableList.of() : Iterables.transform(result.results(), pbToZoneFunction); + return new PageImpl<>(new ZonePageFetcher(serviceOptions, cursor, optionsMap), + cursor, zones); + } catch (RetryHelper.RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Page listChangeRequests(String zoneName, + ChangeRequestListOption... options) { + return listChangeRequests(zoneName, options(), optionMap(options)); + } + + private static Page listChangeRequests(final String zoneName, + final DnsOptions serviceOptions, final Map optionsMap) { + try { + // get a list of changes + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = runWithRetries(new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listChangeRequests(zoneName, optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into change request objects + Iterable changes = result.results() == null + ? ImmutableList.of() + : Iterables.transform(result.results(), + ChangeRequest.fromPbFunction(serviceOptions.service(), zoneName)); + return new PageImpl<>(new ChangeRequestPageFetcher(zoneName, serviceOptions, cursor, + optionsMap), cursor, changes); + } catch (RetryHelper.RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Page listRecordSets(String zoneName, RecordSetListOption... options) { + return listRecordSets(zoneName, options(), optionMap(options)); + } + + private static Page listRecordSets(final String zoneName, + final DnsOptions serviceOptions, final Map optionsMap) { + try { + // get a list of record sets + final DnsRpc rpc = serviceOptions.rpc(); + DnsRpc.ListResult result = runWithRetries( + new Callable>() { + @Override + public DnsRpc.ListResult call() { + return rpc.listRecordSets(zoneName, optionsMap); + } + }, serviceOptions.retryParams(), EXCEPTION_HANDLER); + String cursor = result.pageToken(); + // transform that list into record sets + Iterable recordSets = result.results() == null + ? ImmutableList.of() + : Iterables.transform(result.results(), RecordSet.FROM_PB_FUNCTION); + return new PageImpl<>(new DnsRecordPageFetcher(zoneName, serviceOptions, cursor, optionsMap), + cursor, recordSets); + } catch (RetryHelper.RetryHelperException e) { + throw DnsException.translateAndThrow(e); + } + } + + @Override + public Zone create(final ZoneInfo zoneInfo, Dns.ZoneOption... options) { + final Map optionsMap = optionMap(options); + try { + ManagedZone answer = runWithRetries( + new Callable() { + @Override + public ManagedZone call() { + return dnsRpc.create(zoneInfo.toPb(), optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : Zone.fromPb(this, answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public Zone getZone(final String zoneName, Dns.ZoneOption... options) { + final Map optionsMap = optionMap(options); + try { + ManagedZone answer = runWithRetries( + new Callable() { + @Override + public ManagedZone call() { + return dnsRpc.getZone(zoneName, optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : Zone.fromPb(this, answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public boolean delete(final String zoneName) { + try { + return runWithRetries(new Callable() { + @Override + public Boolean call() { + return dnsRpc.deleteZone(zoneName); + } + }, options().retryParams(), EXCEPTION_HANDLER); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ProjectInfo getProject(Dns.ProjectOption... fields) { + final Map optionsMap = optionMap(fields); + try { + Project answer = runWithRetries( + new Callable() { + @Override + public Project call() { + return dnsRpc.getProject(optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : ProjectInfo.fromPb(answer); // should never be null + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ChangeRequest applyChangeRequest(final String zoneName, + final ChangeRequestInfo changeRequest, ChangeRequestOption... options) { + final Map optionsMap = optionMap(options); + try { + Change answer = runWithRetries( + new Callable() { + @Override + public Change call() { + return dnsRpc.applyChangeRequest(zoneName, changeRequest.toPb(), optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : ChangeRequest.fromPb(this, zoneName, answer); // not null + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + @Override + public ChangeRequest getChangeRequest(final String zoneName, final String changeRequestId, + Dns.ChangeRequestOption... options) { + final Map optionsMap = optionMap(options); + try { + Change answer = runWithRetries( + new Callable() { + @Override + public Change call() { + return dnsRpc.getChangeRequest(zoneName, changeRequestId, optionsMap); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : ChangeRequest.fromPb(this, zoneName, answer); + } catch (RetryHelper.RetryHelperException ex) { + throw DnsException.translateAndThrow(ex); + } + } + + private Map optionMap(Option... options) { + Map temp = Maps.newEnumMap(DnsRpc.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-dns/src/main/java/com/google/cloud/dns/DnsOptions.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsOptions.java new file mode 100644 index 000000000000..6d37a7af5694 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/DnsOptions.java @@ -0,0 +1,114 @@ +/* + * 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.cloud.dns; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.dns.spi.DefaultDnsRpc; +import com.google.cloud.dns.spi.DnsRpc; +import com.google.cloud.dns.spi.DnsRpcFactory; +import com.google.common.collect.ImmutableSet; + +import java.util.Set; + +public class DnsOptions extends ServiceOptions { + + private static final long serialVersionUID = -519128051411747771L; + private static final String GC_DNS_RW = "https://www.googleapis.com/auth/ndev.clouddns.readwrite"; + private static final Set SCOPES = ImmutableSet.of(GC_DNS_RW); + + public static class DefaultDnsFactory implements DnsFactory { + private static final DnsFactory INSTANCE = new DefaultDnsFactory(); + + @Override + public Dns create(DnsOptions options) { + return new DnsImpl(options); + } + } + + public static class DefaultDnsRpcFactory implements DnsRpcFactory { + + private static final DnsRpcFactory INSTANCE = new DefaultDnsRpcFactory(); + + @Override + public DnsRpc create(DnsOptions options) { + return new DefaultDnsRpc(options); + } + } + + public static class Builder extends ServiceOptions.Builder { + + private Builder() { + } + + private Builder(DnsOptions options) { + super(options); + } + + @Override + public DnsOptions build() { + return new DnsOptions(this); + } + } + + private DnsOptions(Builder builder) { + super(DnsFactory.class, DnsRpcFactory.class, builder); + } + + @Override + protected DnsFactory defaultServiceFactory() { + return DefaultDnsFactory.INSTANCE; + } + + @Override + protected DnsRpcFactory defaultRpcFactory() { + return DefaultDnsRpcFactory.INSTANCE; + } + + @Override + protected Set scopes() { + return SCOPES; + } + + @SuppressWarnings("unchecked") + @Override + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Creates a default instance of {@code DnsOptions} with the project ID and credentials inferred + * from the environment. + */ + public static DnsOptions defaultInstance() { + return builder().build(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DnsOptions && baseEquals((DnsOptions) obj); + } + + @Override + public int hashCode() { + return baseHashCode(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/Option.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Option.java new file mode 100644 index 000000000000..7a1f9955be05 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Option.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.cloud.dns.spi.DnsRpc; +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.util.Objects; + +/** + * A base class for options. + */ +abstract class Option implements Serializable { + + private static final long serialVersionUID = -5912727967831484228L; + private final Object value; + private final DnsRpc.Option rpcOption; + + Option(DnsRpc.Option rpcOption, Object value) { + this.rpcOption = checkNotNull(rpcOption); + this.value = value; + } + + Object value() { + return value; + } + + DnsRpc.Option rpcOption() { + return rpcOption; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Option)) { + return false; + } + Option other = (Option) obj; + return Objects.equals(value, other.value) && Objects.equals(rpcOption, other.rpcOption); + } + + @Override + public int hashCode() { + return Objects.hash(value, rpcOption); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("value", value) + .add("rpcOption", rpcOption) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/ProjectInfo.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ProjectInfo.java new file mode 100644 index 000000000000..bc35bacc317d --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ProjectInfo.java @@ -0,0 +1,291 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.Project; +import com.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.Objects; + +/** + * The class provides the Google Cloud DNS information associated with this project. A project is a + * top level container for resources including {@code Zone}s. Projects can be created only in the + * APIs console. + * + * @see Google Cloud DNS documentation + */ +public class ProjectInfo implements Serializable { + + private static final long serialVersionUID = 8696578863323485036L; + private final String id; + private final BigInteger number; + private final Quota quota; + + /** + * This class represents quotas assigned to the {@code ProjectInfo}. + * + * @see Google Cloud DNS + * documentation + */ + public static class Quota implements Serializable { + + private static final long serialVersionUID = 6854685970605363639L; + private final int zones; + private final int resourceRecordsPerRrset; + private final int rrsetAdditionsPerChange; + private final int rrsetDeletionsPerChange; + private final int rrsetsPerZone; + private final int totalRrdataSizePerChange; + + /** + * Creates an instance of {@code Quota}. + * + *

This is the only way of creating an instance of {@code Quota}. As the service does not + * allow for specifying options, quota is an "all-or-nothing object" and we do not need a + * builder. + */ + Quota(int zones, + int resourceRecordsPerRrset, + int rrsetAdditionsPerChange, + int rrsetDeletionsPerChange, + int rrsetsPerZone, + int totalRrdataSizePerChange) { + this.zones = zones; + this.resourceRecordsPerRrset = resourceRecordsPerRrset; + this.rrsetAdditionsPerChange = rrsetAdditionsPerChange; + this.rrsetDeletionsPerChange = rrsetDeletionsPerChange; + this.rrsetsPerZone = rrsetsPerZone; + this.totalRrdataSizePerChange = totalRrdataSizePerChange; + } + + /** + * Returns the maximum allowed number of zones in the project. + */ + public int zones() { + return zones; + } + + /** + * Returns the maximum allowed number of records per {@link RecordSet}. + */ + public int resourceRecordsPerRrset() { + return resourceRecordsPerRrset; + } + + /** + * Returns the maximum allowed number of {@link RecordSet}s to add per {@link + * ChangeRequest}. + */ + public int rrsetAdditionsPerChange() { + return rrsetAdditionsPerChange; + } + + /** + * Returns the maximum allowed number of {@link RecordSet}s to delete per {@link + * ChangeRequest}. + */ + public int rrsetDeletionsPerChange() { + return rrsetDeletionsPerChange; + } + + /** + * Returns the maximum allowed number of {@link RecordSet}s per {@link ZoneInfo} in the + * project. + */ + public int rrsetsPerZone() { + return rrsetsPerZone; + } + + /** + * Returns the maximum allowed size for total records in one ChangesRequest in bytes. + */ + public int totalRrdataSizePerChange() { + return totalRrdataSizePerChange; + } + + @Override + public boolean equals(Object other) { + return (other instanceof Quota) && this.toPb().equals(((Quota) other).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(zones, resourceRecordsPerRrset, rrsetAdditionsPerChange, + rrsetDeletionsPerChange, rrsetsPerZone, totalRrdataSizePerChange); + } + + com.google.api.services.dns.model.Quota toPb() { + com.google.api.services.dns.model.Quota pb = new com.google.api.services.dns.model.Quota(); + pb.setManagedZones(zones); + pb.setResourceRecordsPerRrset(resourceRecordsPerRrset); + pb.setRrsetAdditionsPerChange(rrsetAdditionsPerChange); + pb.setRrsetDeletionsPerChange(rrsetDeletionsPerChange); + pb.setRrsetsPerManagedZone(rrsetsPerZone); + pb.setTotalRrdataSizePerChange(totalRrdataSizePerChange); + return pb; + } + + static Quota fromPb(com.google.api.services.dns.model.Quota pb) { + return new Quota(pb.getManagedZones(), + pb.getResourceRecordsPerRrset(), + pb.getRrsetAdditionsPerChange(), + pb.getRrsetDeletionsPerChange(), + pb.getRrsetsPerManagedZone(), + pb.getTotalRrdataSizePerChange() + ); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("zones", zones) + .add("resourceRecordsPerRrset", resourceRecordsPerRrset) + .add("rrsetAdditionsPerChange", rrsetAdditionsPerChange) + .add("rrsetDeletionsPerChange", rrsetDeletionsPerChange) + .add("rrsetsPerZone", rrsetsPerZone) + .add("totalRrdataSizePerChange", totalRrdataSizePerChange) + .toString(); + } + } + + /** + * A builder for {@code ProjectInfo}. + */ + static class Builder { + private String id; + private BigInteger number; + private Quota quota; + + private Builder() { + } + + /** + * Sets an id of the project. + */ + Builder id(String id) { + this.id = checkNotNull(id); + return this; + } + + /** + * Sets a number of the project. + */ + Builder number(BigInteger number) { + this.number = checkNotNull(number); + return this; + } + + /** + * Sets quotas assigned to the project. + */ + Builder quota(Quota quota) { + this.quota = checkNotNull(quota); + return this; + } + + /** + * Builds an instance of the {@code ProjectInfo}. + */ + ProjectInfo build() { + return new ProjectInfo(this); + } + } + + private ProjectInfo(Builder builder) { + this.id = builder.id; + this.number = builder.number; + this.quota = builder.quota; + } + + /** + * Returns a builder for {@code ProjectInfo}. + */ + static Builder builder() { + return new Builder(); + } + + /** + * Returns the {@code Quota} object which contains quotas assigned to this project. + */ + public Quota quota() { + return quota; + } + + /** + * Returns project number. For internal use only. + */ + BigInteger number() { + return number; + } + + /** + * Returns project id. For internal use only. + */ + String id() { + return id; + } + + Project toPb() { + Project pb = new Project(); + pb.setId(id); + pb.setNumber(number); + if (this.quota != null) { + pb.setQuota(quota.toPb()); + } + return pb; + } + + static ProjectInfo fromPb(Project pb) { + Builder builder = builder(); + if (pb.getId() != null) { + builder.id(pb.getId()); + } + if (pb.getNumber() != null) { + builder.number(pb.getNumber()); + } + if (pb.getQuota() != null) { + builder.quota(Quota.fromPb(pb.getQuota())); + } + return builder.build(); + } + + @Override + public final boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ProjectInfo.class) + && toPb().equals(((ProjectInfo) obj).toPb()); + } + + @Override + public final int hashCode() { + return Objects.hash(id, number, quota); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("number", number) + .add("quota", quota) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/RecordSet.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/RecordSet.java new file mode 100644 index 000000000000..5e061b5164e8 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/RecordSet.java @@ -0,0 +1,319 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.primitives.Ints; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * A class that represents a Google Cloud DNS record set. + * + *

A {@code RecordSet} is the unit of data that will be returned by the DNS servers upon a DNS + * request for a specific domain. The {@code RecordSet} holds the current state of the DNS records + * that make up a zone. You can read the records but you cannot modify them directly. Rather, you + * edit the records in a zone by creating a {@link ChangeRequest}. + * + * @see Google Cloud DNS + * documentation + */ +public final class RecordSet implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public RecordSet apply(ResourceRecordSet pb) { + return RecordSet.fromPb(pb); + } + }; + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public ResourceRecordSet apply(RecordSet recordSet) { + return recordSet.toPb(); + } + }; + private static final long serialVersionUID = 8148009870800115261L; + private final String name; + private final List rrdatas; + private final Integer ttl; // this is in seconds + private final Type type; + + /** + * Enum for the DNS record types supported by Cloud DNS. + * + *

Google Cloud DNS currently supports records of type A, AAAA, CNAME, MX NAPTR, NS, PTR, SOA, + * SPF, SRV, TXT. + * + * @see Cloud DNS + * supported record types + */ + public enum Type { + /** + * Address record, which is used to map host names to their IPv4 address. + */ + A, + /** + * IPv6 Address record, which is used to map host names to their IPv6 address. + */ + AAAA, + /** + * Canonical name record, which is used to alias names. + */ + CNAME, + /** + * Mail exchange record, which is used in routing requests to mail servers. + */ + MX, + /** + * Naming authority pointer record, defined by RFC3403. + */ + NAPTR, + /** + * Name server record, which delegates a DNS zone to an authoritative server. + */ + NS, + /** + * Pointer record, which is often used for reverse DNS lookups. + */ + PTR, + /** + * Start of authority record, which specifies authoritative information about a DNS zone. + */ + SOA, + /** + * Sender policy framework record, which is used in email validation systems. + */ + SPF, + /** + * Service locator record, which is used by some voice over IP, instant messaging protocols and + * other applications. + */ + SRV, + /** + * Text record, which can contain arbitrary text and can also be used to define machine readable + * data such as security or abuse prevention information. + */ + TXT + } + + /** + * A builder for {@link RecordSet}. + */ + public static class Builder { + + private List rrdatas = new LinkedList<>(); + private String name; + private Integer ttl; + private Type type; + + private Builder(String name, Type type) { + this.name = checkNotNull(name); + this.type = checkNotNull(type); + } + + /** + * Creates a builder and pre-populates attributes with the values from the provided {@code + * RecordSet} instance. + */ + private Builder(RecordSet record) { + this.name = record.name; + this.ttl = record.ttl; + this.type = record.type; + this.rrdatas.addAll(record.rrdatas); + } + + /** + * Adds a record to the record set. The records should be as defined in RFC 1035 (section 5) and + * RFC 1034 (section 3.6.1). Examples of records are available in Google DNS documentation. + * + * @see Google + * DNS documentation . + */ + public Builder addRecord(String record) { + this.rrdatas.add(checkNotNull(record)); + return this; + } + + /** + * Removes a record from the set. An exact match is required. + */ + public Builder removeRecord(String record) { + this.rrdatas.remove(checkNotNull(record)); + return this; + } + + /** + * Removes all the records. + */ + public Builder clearRecords() { + this.rrdatas.clear(); + return this; + } + + /** + * Replaces the current records with the provided list of records. + */ + public Builder records(List records) { + this.rrdatas = Lists.newLinkedList(checkNotNull(records)); + return this; + } + + /** + * Sets the name for this record set. For example, www.example.com. + */ + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + /** + * Sets the time that this record can be cached by resolvers. This number must be non-negative. + * The maximum duration must be equivalent to at most {@link Integer#MAX_VALUE} seconds. + * + * @param duration A non-negative number of time units + * @param unit The unit of the ttl parameter + */ + public Builder ttl(int duration, TimeUnit unit) { + checkArgument(duration >= 0, + "Duration cannot be negative. The supplied value was %s.", duration); + checkNotNull(unit); + // we cannot have long because pb does not support it + long converted = unit.toSeconds(duration); + ttl = Ints.checkedCast(converted); + return this; + } + + /** + * The identifier of a supported record type, for example, A, AAAA, MX, TXT, and so on. + */ + public Builder type(Type type) { + this.type = checkNotNull(type); + return this; + } + + /** + * Builds the record set. + */ + public RecordSet build() { + return new RecordSet(this); + } + } + + private RecordSet(Builder builder) { + this.name = builder.name; + this.rrdatas = ImmutableList.copyOf(builder.rrdatas); + this.ttl = builder.ttl; + this.type = builder.type; + } + + /** + * Creates a builder pre-populated with the attribute values of this instance. + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates a {@code RecordSet} builder for the given {@code name} and {@code type}. + */ + public static Builder builder(String name, Type type) { + return new Builder(name, type); + } + + /** + * Returns the user-assigned name of this record set. + */ + public String name() { + return name; + } + + /** + * Returns a list of records stored in this record set. + */ + public List records() { + return rrdatas; + } + + /** + * Returns the number of seconds that this record set can be cached by resolvers. + */ + public Integer ttl() { + return ttl; + } + + /** + * Returns the type of this record set. + */ + public Type type() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(name, rrdatas, ttl, type); + } + + @Override + public boolean equals(Object obj) { + return obj == this + || obj instanceof RecordSet + && Objects.equals(this.toPb(), ((RecordSet) obj).toPb()); + } + + ResourceRecordSet toPb() { + ResourceRecordSet pb = new ResourceRecordSet(); + pb.setName(this.name()); + pb.setRrdatas(this.records()); + pb.setTtl(this.ttl()); + pb.setType(this.type().name()); + return pb; + } + + static RecordSet fromPb(ResourceRecordSet pb) { + Builder builder = builder(pb.getName(), Type.valueOf(pb.getType())); + if (pb.getRrdatas() != null) { + builder.records(pb.getRrdatas()); + } + if (pb.getTtl() != null) { + builder.ttl(pb.getTtl(), TimeUnit.SECONDS); + } + return builder.build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name()) + .add("rrdatas", records()) + .add("ttl", ttl()) + .add("type", type()) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/Zone.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Zone.java new file mode 100644 index 000000000000..a01f7212d28e --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/Zone.java @@ -0,0 +1,225 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.ManagedZone; +import com.google.cloud.Page; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.List; +import java.util.Objects; + +/** + * A Google Cloud DNS Zone object. + * + *

A zone is the container for all of your record sets that share the same DNS name prefix, for + * example, example.com. Zones are automatically assigned a set of name servers when they are + * created to handle responding to DNS queries for that zone. A zone has quotas for the number of + * record sets that it can include. + * + * @see Google Cloud DNS managed zone + * documentation + */ +public class Zone extends ZoneInfo { + + private static final long serialVersionUID = -5817771337847861598L; + private final DnsOptions options; + private transient Dns dns; + + /** + * Builder for {@code Zone}. + */ + public static class Builder extends ZoneInfo.Builder { + private final Dns dns; + private final ZoneInfo.BuilderImpl infoBuilder; + + private Builder(Zone zone) { + this.dns = zone.dns; + this.infoBuilder = new ZoneInfo.BuilderImpl(zone); + } + + @Override + public Builder name(String name) { + infoBuilder.name(name); + return this; + } + + @Override + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); + return this; + } + + @Override + Builder creationTimeMillis(long creationTimeMillis) { + infoBuilder.creationTimeMillis(creationTimeMillis); + return this; + } + + @Override + public Builder dnsName(String dnsName) { + infoBuilder.dnsName(dnsName); + return this; + } + + @Override + public Builder description(String description) { + infoBuilder.description(description); + return this; + } + + @Override + Builder nameServerSet(String nameServerSet) { + infoBuilder.nameServerSet(nameServerSet); + return this; + } + + @Override + Builder nameServers(List nameServers) { + infoBuilder.nameServers(nameServers); // infoBuilder makes a copy + return this; + } + + @Override + public Zone build() { + return new Zone(dns, infoBuilder); + } + } + + Zone(Dns dns, ZoneInfo.BuilderImpl infoBuilder) { + super(infoBuilder); + this.dns = dns; + this.options = dns.options(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Retrieves the latest information about the zone. The method retrieves the zone by name. + * + * @param options optional restriction on what fields should be fetched + * @return zone object containing updated information or {@code null} if not not found + * @throws DnsException upon failure + */ + public Zone reload(Dns.ZoneOption... options) { + return dns.getZone(name(), options); + } + + /** + * Deletes the zone. The method deletes the zone by name. + * + * @return {@code true} is zone was found and deleted and {@code false} otherwise + * @throws DnsException upon failure + */ + public boolean delete() { + return dns.delete(name()); + } + + /** + * Lists all {@link RecordSet}s associated with this zone. The method searches for zone by name. + * + * @param options optional restriction on listing and fields of {@link RecordSet}s returned + * @return a page of record sets + * @throws DnsException upon failure or if the zone is not found + */ + public Page listRecordSets(Dns.RecordSetListOption... options) { + return dns.listRecordSets(name(), options); + } + + /** + * Submits {@link ChangeRequestInfo} to the service for it to applied to this zone. The method + * searches for zone by name. + * + * @param options optional restriction on what fields of {@link ChangeRequest} should be returned + * @return ChangeRequest with server-assigned ID + * @throws DnsException upon failure or if the zone is not found + */ + public ChangeRequest applyChangeRequest(ChangeRequestInfo changeRequest, + Dns.ChangeRequestOption... options) { + checkNotNull(changeRequest); + return dns.applyChangeRequest(name(), changeRequest, options); + } + + /** + * Retrieves an updated information about a change request previously submitted to be applied to + * this zone. Returns a {@link ChangeRequest} or {@code null} if the change request was not found. + * Throws {@link DnsException} if the zone is not found. + * + * @param options optional restriction on what fields of {@link ChangeRequest} should be returned + * @return updated ChangeRequest + * @throws DnsException upon failure or if the zone is not found + * @throws NullPointerException if {@code changeRequestId} is null + */ + public ChangeRequest getChangeRequest(String changeRequestId, + Dns.ChangeRequestOption... options) { + checkNotNull(changeRequestId); + return dns.getChangeRequest(name(), changeRequestId, options); + } + + /** + * Retrieves all change requests for this zone. The method searches for zone by name. Returns a + * page of {@link ChangeRequest}s. + * + * @param options optional restriction on listing and fields to be returned + * @return a page of change requests + * @throws DnsException upon failure or if the zone is not found + */ + public Page listChangeRequests(Dns.ChangeRequestListOption... options) { + return dns.listChangeRequests(name(), options); + } + + /** + * Returns the {@link Dns} service object associated with this zone. + */ + public Dns dns() { + return this.dns; + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Zone.class)) { + return false; + } + Zone other = (Zone) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); + } + + @Override + public final int hashCode() { + return Objects.hash(super.hashCode(), options); + } + + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.dns = options.service(); + } + + static Zone fromPb(Dns dns, ManagedZone zone) { + ZoneInfo info = ZoneInfo.fromPb(zone); + return new Zone(dns, new ZoneInfo.BuilderImpl(info)); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/ZoneInfo.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ZoneInfo.java new file mode 100644 index 000000000000..d6178479b68b --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/ZoneInfo.java @@ -0,0 +1,320 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.services.dns.model.ManagedZone; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.joda.time.DateTime; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.List; +import java.util.Objects; + +/** + * A {@code Zone} represents a DNS zone hosted by the Google Cloud DNS service. A zone is a subtree + * of the DNS namespace under one administrative responsibility. See Google Cloud DNS documentation for + * more information. + */ +public class ZoneInfo implements Serializable { + + private static final long serialVersionUID = -5313169712036079818L; + private final String name; + private final String generatedId; + private final Long creationTimeMillis; + private final String dnsName; + private final String description; + private final String nameServerSet; + private final List nameServers; + + /** + * Builder for {@code ZoneInfo}. + */ + public abstract static class Builder { + /** + * Sets a mandatory user-provided name for the zone. It must be unique within the project. + */ + public abstract Builder name(String name); + + /** + * Sets service-generated id for the zone. + */ + abstract Builder generatedId(String generatedId); + + /** + * Sets the time when this zone was created. + */ + abstract Builder creationTimeMillis(long creationTimeMillis); + + /** + * Sets a mandatory DNS name of this zone, for instance "example.com.". + */ + public abstract Builder dnsName(String dnsName); + + /** + * Sets a mandatory description for this zone. The value is a string of at most 1024 characters + * which has no effect on the zone's function. + */ + public abstract Builder description(String description); + + /** + * Optionally specifies the NameServerSet for this zone. A NameServerSet is a set of DNS name + * servers that all host the same zones. Most users will not need to specify this value. + */ + abstract Builder nameServerSet(String nameServerSet); + // this should not be included in tooling as per the service owners + + /** + * Sets a list of servers that hold the information about the zone. This information is provided + * by Google Cloud DNS and is read only. + */ + abstract Builder nameServers(List nameServers); + + /** + * Builds the instance of {@code ZoneInfo} based on the information set by this builder. + */ + public abstract ZoneInfo build(); + } + + static class BuilderImpl extends Builder { + private String name; + private String generatedId; + private Long creationTimeMillis; + private String dnsName; + private String description; + private String nameServerSet; + private List nameServers; + + private BuilderImpl(String name) { + this.name = checkNotNull(name); + } + + /** + * Creates a builder from an existing ZoneInfo object. + */ + BuilderImpl(ZoneInfo info) { + this.name = info.name; + this.generatedId = info.generatedId; + this.creationTimeMillis = info.creationTimeMillis; + this.dnsName = info.dnsName; + this.description = info.description; + this.nameServerSet = info.nameServerSet; + if (info.nameServers != null) { + this.nameServers = ImmutableList.copyOf(info.nameServers); + } + } + + @Override + public Builder name(String name) { + this.name = checkNotNull(name); + return this; + } + + @Override + Builder generatedId(String generatedId) { + this.generatedId = generatedId; + return this; + } + + @Override + Builder creationTimeMillis(long creationTimeMillis) { + this.creationTimeMillis = creationTimeMillis; + return this; + } + + @Override + public Builder dnsName(String dnsName) { + this.dnsName = checkNotNull(dnsName); + return this; + } + + @Override + public Builder description(String description) { + this.description = checkNotNull(description); + return this; + } + + @Override + Builder nameServerSet(String nameServerSet) { + this.nameServerSet = checkNotNull(nameServerSet); + return this; + } + + @Override + Builder nameServers(List nameServers) { + checkNotNull(nameServers); + this.nameServers = Lists.newLinkedList(nameServers); + return this; + } + + @Override + public ZoneInfo build() { + return new ZoneInfo(this); + } + } + + ZoneInfo(BuilderImpl builder) { + this.name = builder.name; + this.generatedId = builder.generatedId; + this.creationTimeMillis = builder.creationTimeMillis; + this.dnsName = builder.dnsName; + this.description = builder.description; + this.nameServerSet = builder.nameServerSet; + this.nameServers = builder.nameServers == null + ? null : ImmutableList.copyOf(builder.nameServers); + } + + /** + * Returns a ZoneInfo object with assigned {@code name}, {@code dnsName} and {@code description}. + */ + public static ZoneInfo of(String name, String dnsName, String description) { + return new BuilderImpl(name).dnsName(dnsName).description(description).build(); + } + + /** + * Returns the user-defined name of the zone. + */ + public String name() { + return name; + } + + /** + * Returns the service-generated id for this zone. + */ + public String generatedId() { + return generatedId; + } + + /** + * Returns the time when this zone was created on the server. + */ + public Long creationTimeMillis() { + return creationTimeMillis; + } + + /** + * Returns the DNS name of this zone, for instance "example.com.". + */ + public String dnsName() { + return dnsName; + } + + /** + * Returns the description of this zone. + */ + public String description() { + return description; + } + + /** + * Returns the optionally specified set of DNS name servers that all host this zone. This value is + * set only for specific use cases and is left empty for vast majority of users. + */ + public String nameServerSet() { + return nameServerSet; + } + + /** + * The nameservers that the zone should be delegated to. This is defined by the Google DNS cloud. + */ + public List nameServers() { + return nameServers == null ? ImmutableList.of() : nameServers; + } + + /** + * Returns a builder for {@code ZoneInfo} prepopulated with the metadata of this zone. + */ + public Builder toBuilder() { + return new BuilderImpl(this); + } + + ManagedZone toPb() { + ManagedZone pb = + new ManagedZone(); + pb.setDescription(this.description()); + pb.setDnsName(this.dnsName()); + if (this.generatedId() != null) { + pb.setId(new BigInteger(this.generatedId())); + } + pb.setName(this.name()); + pb.setNameServers(this.nameServers); // do use real attribute value which may be null + pb.setNameServerSet(this.nameServerSet()); + if (this.creationTimeMillis() != null) { + pb.setCreationTime(ISODateTimeFormat.dateTime() + .withZoneUTC() + .print(this.creationTimeMillis())); + } + return pb; + } + + static ZoneInfo fromPb(ManagedZone pb) { + Builder builder = new BuilderImpl(pb.getName()); + if (pb.getDescription() != null) { + builder.description(pb.getDescription()); + } + if (pb.getDnsName() != null) { + builder.dnsName(pb.getDnsName()); + } + if (pb.getId() != null) { + builder.generatedId(pb.getId().toString()); + } + if (pb.getNameServers() != null) { + builder.nameServers(pb.getNameServers()); + } + if (pb.getNameServerSet() != null) { + builder.nameServerSet(pb.getNameServerSet()); + } + if (pb.getCreationTime() != null) { + builder.creationTimeMillis(DateTime.parse(pb.getCreationTime()).getMillis()); + } + return builder.build(); + } + + @Override + public boolean equals(Object obj) { + return obj == this + || obj != null + && obj.getClass().equals(ZoneInfo.class) + && Objects.equals(toPb(), ((ZoneInfo) obj).toPb()); + } + + @Override + public int hashCode() { + return Objects.hash(name, generatedId, creationTimeMillis, dnsName, description, nameServerSet, + nameServers); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name()) + .add("generatedId", generatedId()) + .add("description", description()) + .add("dnsName", dnsName()) + .add("nameServerSet", nameServerSet()) + .add("nameServers", nameServers()) + .add("creationTimeMillis", creationTimeMillis()) + .toString(); + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/package-info.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/package-info.java new file mode 100644 index 000000000000..cdcce0d1ef90 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/package-info.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 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. + */ + +/** + * A client to the Google Cloud DNS. + * + *

+ * Here are two simple usage examples from within Compute/App Engine. + * + * The first snippet shows how to create a zone resource. The complete source code can be found on + * + * CreateAndListZones.java. Note that you need to replace the {@code domainName} with a domain + * name that you own and the ownership of which you verified with Google. + * + *

 {@code
+ * Dns dns = DnsOptions.defaultInstance().service();
+ * String zoneName = "my-unique-zone";
+ * String domainName = "someexampledomain.com.";
+ * String description = "This is a gcloud-java-dns sample zone.";
+ * ZoneInfo zoneInfo = ZoneInfo.of(zoneName, domainName, description);
+ * Zone createdZone = dns.create(zoneInfo);
+ * } 
+ * + *

+ * The second example shows how to create records inside a zone. The complete code can be found on + * + * CreateAndListDnsRecords.java. + * + *

 {@code
+ * Dns dns = DnsOptions.defaultInstance().service();
+ * String zoneName = "my-unique-zone";
+ * Zone zone = dns.getZone(zoneName);
+ * String ip = "12.13.14.15";
+ * RecordSet toCreate = RecordSet.builder("www.someexampledomain.com.", RecordSet.Type.A)
+ *   .ttl(24, TimeUnit.HOURS)
+ *   .addRecord(ip)
+ *   .build();
+ * ChangeRequestInfo changeRequest = ChangeRequestInfo.builder().add(toCreate).build();
+ * zone.applyChangeRequest(changeRequest);
+ * } 
+ * + *

+ * When using gcloud-java from outside of App/Compute Engine, you have to + * specify a + * project ID and + * provide + * credentials. + * + * @see Google Cloud DNS + */ +package com.google.cloud.dns; diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DefaultDnsRpc.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DefaultDnsRpc.java new file mode 100644 index 000000000000..08f14a0ba254 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DefaultDnsRpc.java @@ -0,0 +1,197 @@ +package com.google.cloud.dns.spi; + +import static com.google.cloud.dns.spi.DnsRpc.ListResult.of; +import static com.google.cloud.dns.spi.DnsRpc.Option.DNS_NAME; +import static com.google.cloud.dns.spi.DnsRpc.Option.DNS_TYPE; +import static com.google.cloud.dns.spi.DnsRpc.Option.FIELDS; +import static com.google.cloud.dns.spi.DnsRpc.Option.NAME; +import static com.google.cloud.dns.spi.DnsRpc.Option.PAGE_SIZE; +import static com.google.cloud.dns.spi.DnsRpc.Option.PAGE_TOKEN; +import static com.google.cloud.dns.spi.DnsRpc.Option.SORTING_ORDER; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; + +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.dns.Dns; +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ChangesListResponse; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ManagedZonesListResponse; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.api.services.dns.model.ResourceRecordSetsListResponse; +import com.google.cloud.dns.DnsException; +import com.google.cloud.dns.DnsOptions; + +import java.io.IOException; +import java.util.Map; + +/** + * A default implementation of the DnsRpc interface. + */ +public class DefaultDnsRpc implements DnsRpc { + + private static final String SORT_BY = "changeSequence"; + private final Dns dns; + private final DnsOptions options; + + private static DnsException translate(IOException exception, boolean idempotent) { + return new DnsException(exception, idempotent); + } + + /** + * Constructs an instance of this rpc client with provided {@link DnsOptions}. + */ + public DefaultDnsRpc(DnsOptions options) { + HttpTransport transport = options.httpTransportFactory().create(); + HttpRequestInitializer initializer = options.httpRequestInitializer(); + this.dns = new Dns.Builder(transport, new JacksonFactory(), initializer) + .setRootUrl(options.host()) + .setApplicationName(options.applicationName()) + .build(); + this.options = options; + } + + @Override + public ManagedZone create(ManagedZone zone, Map options) throws DnsException { + try { + return dns.managedZones() + .create(this.options.projectId(), zone) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + // todo this can cause misleading report of a failure, intended to be fixed within #924 + throw translate(ex, true); + } + } + + @Override + public ManagedZone getZone(String zoneName, Map options) throws DnsException { + // just fields option + try { + return dns.managedZones().get(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + DnsException serviceException = translate(ex, true); + if (serviceException.code() == HTTP_NOT_FOUND) { + return null; + } + throw serviceException; + } + } + + @Override + public ListResult listZones(Map options) throws DnsException { + // fields, page token, page size + try { + ManagedZonesListResponse zoneList = dns.managedZones().list(this.options.projectId()) + .setFields(FIELDS.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setDnsName(DNS_NAME.getString(options)) + .setPageToken(PAGE_TOKEN.getString(options)) + .execute(); + return of(zoneList.getNextPageToken(), zoneList.getManagedZones()); + } catch (IOException ex) { + throw translate(ex, true); + } + } + + @Override + public boolean deleteZone(String zoneName) throws DnsException { + try { + dns.managedZones().delete(this.options.projectId(), zoneName).execute(); + return true; + } catch (IOException ex) { + DnsException serviceException = translate(ex, false); + if (serviceException.code() == HTTP_NOT_FOUND) { + return false; + } + throw serviceException; + } + } + + @Override + public ListResult listRecordSets(String zoneName, Map options) + throws DnsException { + // options are fields, page token, dns name, type + try { + ResourceRecordSetsListResponse response = dns.resourceRecordSets() + .list(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .setPageToken(PAGE_TOKEN.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setName(NAME.getString(options)) + .setType(DNS_TYPE.getString(options)) + .execute(); + return of(response.getNextPageToken(), response.getRrsets()); + } catch (IOException ex) { + throw translate(ex, true); + } + } + + @Override + public Project getProject(Map options) throws DnsException { + try { + return dns.projects().get(this.options.projectId()) + .setFields(FIELDS.getString(options)).execute(); + } catch (IOException ex) { + throw translate(ex, true); + } + } + + @Override + public Change applyChangeRequest(String zoneName, Change changeRequest, Map options) + throws DnsException { + try { + return dns.changes().create(this.options.projectId(), zoneName, changeRequest) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + throw translate(ex, false); + } + } + + @Override + public Change getChangeRequest(String zoneName, String changeRequestId, Map options) + throws DnsException { + try { + return dns.changes().get(this.options.projectId(), zoneName, changeRequestId) + .setFields(FIELDS.getString(options)) + .execute(); + } catch (IOException ex) { + DnsException serviceException = translate(ex, true); + if (serviceException.code() == HTTP_NOT_FOUND) { + if ("entity.parameters.changeId".equals(serviceException.location()) + || (serviceException.getMessage() != null + && serviceException.getMessage().contains("parameters.changeId"))) { + // the change id was not found, but the zone exists + return null; + } + // the zone does not exist, so throw an exception + } + throw serviceException; + } + } + + @Override + public ListResult listChangeRequests(String zoneName, Map options) + throws DnsException { + // options are fields, page token, page size, sort order + try { + Dns.Changes.List request = dns.changes().list(this.options.projectId(), zoneName) + .setFields(FIELDS.getString(options)) + .setMaxResults(PAGE_SIZE.getInt(options)) + .setPageToken(PAGE_TOKEN.getString(options)); + if (SORTING_ORDER.getString(options) != null) { + // todo check and change if more sorting options are implemented, issue #604 + request = request.setSortBy(SORT_BY).setSortOrder(SORTING_ORDER.getString(options)); + } + ChangesListResponse response = request.execute(); + return of(response.getNextPageToken(), response.getChanges()); + } catch (IOException ex) { + throw translate(ex, true); + } + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpc.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpc.java new file mode 100644 index 000000000000..ba74f14f0527 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpc.java @@ -0,0 +1,174 @@ +/* + * Copyright 2016 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.cloud.dns.spi; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.cloud.dns.DnsException; +import com.google.common.collect.ImmutableList; + +import java.util.Map; + +public interface DnsRpc { + + enum Option { + FIELDS("fields"), + PAGE_SIZE("maxResults"), + PAGE_TOKEN("pageToken"), + DNS_NAME("dnsName"), + NAME("name"), + DNS_TYPE("type"), + SORTING_ORDER("sortOrder"); + + private final String value; + + Option(String value) { + this.value = value; + } + + public String value() { + return value; + } + + @SuppressWarnings("unchecked") + T get(Map options) { + return (T) options.get(this); + } + + String getString(Map options) { + return get(options); + } + + Integer getInt(Map options) { + return get(options); + } + } + + class ListResult { + + private final Iterable results; + private final String pageToken; + + public ListResult(String pageToken, Iterable results) { + this.results = ImmutableList.copyOf(results); + this.pageToken = pageToken; + } + + public static ListResult of(String pageToken, Iterable list) { + return new ListResult<>(pageToken, list); + } + + public Iterable results() { + return results; + } + + public String pageToken() { + return pageToken; + } + } + + /** + * Creates a new zone. + * + * @param zone a zone to be created + * @param options a map of options for the service call + * @return Updated {@code ManagedZone} object + * @throws DnsException upon failure + */ + ManagedZone create(ManagedZone zone, Map options) throws DnsException; + + /** + * Retrieves and returns an existing zone. + * + * @param zoneName name of the zone to be returned + * @param options a map of options for the service call + * @return a zone or {@code null} if not found + * @throws DnsException upon failure + */ + ManagedZone getZone(String zoneName, Map options) throws DnsException; + + /** + * Lists the zones that exist within the project. + * + * @param options a map of options for the service call + * @throws DnsException upon failure + */ + ListResult listZones(Map options) throws DnsException; + + /** + * Deletes the zone identified by the name. + * + * @return {@code true} if the zone was deleted and {@code false} otherwise + * @throws DnsException upon failure + */ + boolean deleteZone(String zoneName) throws DnsException; + + /** + * Lists record sets for a given zone. + * + * @param zoneName name of the zone to be listed + * @param options a map of options for the service call + * @throws DnsException upon failure or if zone was not found + */ + ListResult listRecordSets(String zoneName, Map options) + throws DnsException; + + /** + * Returns information about the current project. + * + * @param options a map of options for the service call + * @return up-to-date project information + * @throws DnsException upon failure or if the project is not found + */ + Project getProject(Map options) throws DnsException; + + /** + * Applies change request to a zone. + * + * @param zoneName the name of a zone to which the {@code Change} should be applied + * @param changeRequest change to be applied + * @param options a map of options for the service call + * @return updated change object with server-assigned ID + * @throws DnsException upon failure or if zone was not found + */ + Change applyChangeRequest(String zoneName, Change changeRequest, Map options) + throws DnsException; + + /** + * Returns an existing change request. + * + * @param zoneName the name of a zone to which the {@code Change} was be applied + * @param changeRequestId the unique id assigned to the change by the server + * @param options a map of options for the service call + * @return up-to-date change object or {@code null} if change was not found + * @throws DnsException upon failure or if zone was not found + */ + Change getChangeRequest(String zoneName, String changeRequestId, Map options) + throws DnsException; + + /** + * List existing change requests for a zone. + * + * @param zoneName the name of a zone to which the {@code Change}s were be applied + * @param options a map of options for the service call + * @throws DnsException upon failure or if zone was not found + */ + ListResult listChangeRequests(String zoneName, Map options) + throws DnsException; +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpcFactory.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpcFactory.java new file mode 100644 index 000000000000..f54f9d02b41e --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/spi/DnsRpcFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 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.cloud.dns.spi; + +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.spi.ServiceRpcFactory; + +/** + * An interface for DnsRpc factory. Implementation will be loaded via {@link + * java.util.ServiceLoader}. + */ +public interface DnsRpcFactory extends ServiceRpcFactory { +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/LocalDnsHelper.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/LocalDnsHelper.java new file mode 100644 index 000000000000..1f35193409ee --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/LocalDnsHelper.java @@ -0,0 +1,1266 @@ +/* + * Copyright 2016 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.cloud.dns.testing; + +import static com.google.common.net.InetAddresses.isInetAddress; +import static java.net.HttpURLConnection.HTTP_NO_CONTENT; +import static java.net.HttpURLConnection.HTTP_OK; + +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson.JacksonFactory; +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.Quota; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.cloud.dns.DnsOptions; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.io.ByteStreams; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import org.joda.time.format.ISODateTimeFormat; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Random; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; + +/** + * A local Google Cloud DNS mock. + * + *

The mock runs in a separate thread, listening for HTTP requests on the local machine at an + * ephemeral port. + * + *

While the mock attempts to simulate the service, there are some differences in the behaviour. + * The mock will accept any project ID and never returns a notFound or another error because of + * project ID. It assumes that all project IDs exist and that the user has all the necessary + * privileges to manipulate any project. Similarly, the local simulation does not require + * verification of domain name ownership. Any request for creating a managed zone will be approved. + * The mock does not track quota and will allow the user to exceed it. The mock provides only basic + * validation of the DNS data for record sets of type A and AAAA. It does not validate any other + * record set types. + */ +public class LocalDnsHelper { + + private final ConcurrentSkipListMap projects + = new ConcurrentSkipListMap<>(); + private static final URI BASE_CONTEXT; + private static final Logger log = Logger.getLogger(LocalDnsHelper.class.getName()); + private static final JsonFactory jsonFactory = new JacksonFactory(); + private static final Random ID_GENERATOR = new Random(); + private static final String VERSION = "v1"; + private static final String CONTEXT = "/dns/" + VERSION + "/projects"; + private static final Set ENCODINGS = ImmutableSet.of("gzip", "x-gzip"); + private static final List TYPES = ImmutableList.of("A", "AAAA", "CNAME", "MX", "NAPTR", + "NS", "PTR", "SOA", "SPF", "SRV", "TXT"); + private static final TreeSet FORBIDDEN = Sets.newTreeSet( + ImmutableList.of("google.com.", "com.", "example.com.", "net.", "org.")); + private static final Pattern ZONE_NAME_RE = Pattern.compile("[a-z][a-z0-9-]*"); + private static final ScheduledExecutorService EXECUTORS = + Executors.newScheduledThreadPool(2, Executors.defaultThreadFactory()); + private static final String PROJECT_ID = "dummyprojectid"; + + static { + try { + BASE_CONTEXT = new URI(CONTEXT); + } catch (URISyntaxException e) { + throw new IllegalArgumentException( + "Could not initialize LocalDnsHelper due to URISyntaxException.", e); + } + } + + private final long delayChange; + private final HttpServer server; + private final int port; + + /** + * For matching URLs to operations. + */ + private enum CallRegex { + CHANGE_CREATE("POST", CONTEXT + "/[^/]+/managedZones/[^/]+/changes"), + CHANGE_GET("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/changes/[^/]+"), + CHANGE_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/changes"), + ZONE_CREATE("POST", CONTEXT + "/[^/]+/managedZones"), + ZONE_DELETE("DELETE", CONTEXT + "/[^/]+/managedZones/[^/]+"), + ZONE_GET("GET", CONTEXT + "/[^/]+/managedZones/[^/]+"), + ZONE_LIST("GET", CONTEXT + "/[^/]+/managedZones"), + PROJECT_GET("GET", CONTEXT + "/[^/]+"), + RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets"); + + private final String method; + private final String pathRegex; + + CallRegex(String method, String pathRegex) { + this.pathRegex = pathRegex; + this.method = method; + } + } + + /** + * Associates a project with a collection of ManagedZones. + */ + static class ProjectContainer { + private final Project project; + private final ConcurrentSkipListMap zones = + new ConcurrentSkipListMap<>(); + + ProjectContainer(Project project) { + this.project = project; + } + + Project project() { + return project; + } + + ConcurrentSkipListMap zones() { + return zones; + } + } + + /** + * Associates a zone with a collection of changes and dns records. + */ + static class ZoneContainer { + private final ManagedZone zone; + private final AtomicReference> + dnsRecords = new AtomicReference<>(ImmutableSortedMap.of()); + private final ConcurrentLinkedQueue changes = new ConcurrentLinkedQueue<>(); + + ZoneContainer(ManagedZone zone) { + this.zone = zone; + this.dnsRecords.set(ImmutableSortedMap.of()); + } + + ManagedZone zone() { + return zone; + } + + AtomicReference> dnsRecords() { + return dnsRecords; + } + + ConcurrentLinkedQueue changes() { + return changes; + } + + Change findChange(String changeId) { + for (Change current : changes) { + if (changeId.equals(current.getId())) { + return current; + } + } + return null; + } + } + + static class Response { + private final int code; + private final String body; + + Response(int code, String body) { + this.code = code; + this.body = body; + } + + int code() { + return code; + } + + String body() { + return body; + } + } + + private enum Error { + REQUIRED(400, "global", "required", "REQUIRED"), + INTERNAL_ERROR(500, "global", "internalError", "INTERNAL_ERROR"), + BAD_REQUEST(400, "global", "badRequest", "BAD_REQUEST"), + INVALID(400, "global", "invalid", "INVALID"), + CONTAINER_NOT_EMPTY(400, "global", "containerNotEmpty", "CONTAINER_NOT_EMPTY"), + NOT_AVAILABLE(400, "global", "managedZoneDnsNameNotAvailable", "NOT_AVAILABLE"), + NOT_FOUND(404, "global", "notFound", "NOT_FOUND"), + ALREADY_EXISTS(409, "global", "alreadyExists", "ALREADY_EXISTS"), + CONDITION_NOT_MET(412, "global", "conditionNotMet", "CONDITION_NOT_MET"), + INVALID_ZONE_APEX(400, "global", "invalidZoneApex", "INVALID_ZONE_APEX"); + + private final int code; + private final String domain; + private final String reason; + private final String status; + + Error(int code, String domain, String reason, String status) { + this.code = code; + this.domain = domain; + this.reason = reason; + this.status = status; + } + + Response response(String message) { + try { + return new Response(code, toJson(message)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response("Error when generating JSON error response."); + } + } + + private String toJson(String message) throws IOException { + Map errors = new HashMap<>(); + errors.put("domain", domain); + errors.put("message", message); + errors.put("reason", reason); + Map args = new HashMap<>(); + args.put("errors", ImmutableList.of(errors)); + args.put("code", code); + args.put("message", message); + args.put("status", status); + return jsonFactory.toString(ImmutableMap.of("error", args)); + } + } + + private class RequestHandler implements HttpHandler { + + private Response pickHandler(HttpExchange exchange, CallRegex regex) { + URI relative = BASE_CONTEXT.relativize(exchange.getRequestURI()); + String path = relative.getPath(); + String[] tokens = path.split("/"); + String projectId = tokens.length > 0 ? tokens[0] : null; + String zoneName = tokens.length > 2 ? tokens[2] : null; + String changeId = tokens.length > 4 ? tokens[4] : null; + String query = relative.getQuery(); + switch (regex) { + case CHANGE_GET: + return getChange(projectId, zoneName, changeId, query); + case CHANGE_LIST: + return listChanges(projectId, zoneName, query); + case ZONE_GET: + return getZone(projectId, zoneName, query); + case ZONE_DELETE: + return deleteZone(projectId, zoneName); + case ZONE_LIST: + return listZones(projectId, query); + case PROJECT_GET: + return getProject(projectId, query); + case RECORD_LIST: + return listDnsRecords(projectId, zoneName, query); + case ZONE_CREATE: + try { + return handleZoneCreate(exchange, projectId, query); + } catch (IOException ex) { + return Error.BAD_REQUEST.response(ex.getMessage()); + } + case CHANGE_CREATE: + try { + return handleChangeCreate(exchange, projectId, zoneName, query); + } catch (IOException ex) { + return Error.BAD_REQUEST.response(ex.getMessage()); + } + default: + return Error.INTERNAL_ERROR.response("Operation without a handler."); + } + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + String requestMethod = exchange.getRequestMethod(); + String rawPath = exchange.getRequestURI().getRawPath(); + for (CallRegex regex : CallRegex.values()) { + if (requestMethod.equals(regex.method) && rawPath.matches(regex.pathRegex)) { + Response response = pickHandler(exchange, regex); + writeResponse(exchange, response); + return; + } + } + writeResponse(exchange, Error.NOT_FOUND.response(String.format( + "The url %s for %s method does not match any API call.", + requestMethod, exchange.getRequestURI()))); + } + + /** + * @throws IOException if the request cannot be parsed. + */ + private Response handleChangeCreate(HttpExchange exchange, String projectId, String zoneName, + String query) throws IOException { + String requestBody = decodeContent(exchange.getRequestHeaders(), exchange.getRequestBody()); + Change change; + try { + change = jsonFactory.fromString(requestBody, Change.class); + } catch (IllegalArgumentException ex) { + return Error.REQUIRED.response( + "The 'entity.change' parameter is required but was missing."); + } + String[] fields = OptionParsers.parseGetOptions(query); + return createChange(projectId, zoneName, change, fields); + } + + /** + * @throws IOException if the request cannot be parsed. + */ + private Response handleZoneCreate(HttpExchange exchange, String projectId, String query) + throws IOException { + String requestBody = decodeContent(exchange.getRequestHeaders(), exchange.getRequestBody()); + ManagedZone zone; + try { + zone = jsonFactory.fromString(requestBody, ManagedZone.class); + } catch (IllegalArgumentException ex) { + return Error.REQUIRED.response( + "The 'entity.managedZone' parameter is required but was missing."); + } + String[] options = OptionParsers.parseGetOptions(query); + return createZone(projectId, zone, options); + } + } + + private LocalDnsHelper(long delay) { + this.delayChange = delay; + try { + server = HttpServer.create(new InetSocketAddress(0), 0); + port = server.getAddress().getPort(); + server.createContext(CONTEXT, new RequestHandler()); + } catch (IOException e) { + throw new RuntimeException("Could not bind the mock DNS server.", e); + } + } + + /** + * Accessor for testing purposes. + */ + ConcurrentSkipListMap projects() { + return projects; + } + + /** + * Creates new {@code LocalDnsHelper} instance that listens to requests on the local machine. This + * instance processes changes in separate thread. The parameter determines how long a thread + * should wait before processing a change. If it is set to 0, the threading is turned off and the + * mock will behave synchronously. + * + * @param delay delay for processing changes in ms or 0 for synchronous processing + */ + public static LocalDnsHelper create(Long delay) { + return new LocalDnsHelper(delay); + } + + /** + * Returns a {@link DnsOptions} instance that sets the host to use the mock server. + */ + public DnsOptions options() { + return DnsOptions.builder().projectId(PROJECT_ID).host("http://localhost:" + port).build(); + } + + /** + * Starts the thread that runs the local DNS server. + */ + public void start() { + server.start(); + } + + /** + * Stops the thread that runs the mock DNS server. + */ + public void stop() { + server.stop(1); + } + + private static void writeResponse(HttpExchange exchange, Response response) { + exchange.getResponseHeaders().set("Content-type", "application/json; charset=UTF-8"); + OutputStream outputStream = exchange.getResponseBody(); + try { + exchange.getResponseHeaders().add("Connection", "close"); + exchange.sendResponseHeaders(response.code(), response.body().length()); + if (response.code() != 204) { + // the server automatically sends headers and closes output stream when 204 is returned + outputStream.write(response.body().getBytes(StandardCharsets.UTF_8)); + } + outputStream.close(); + } catch (IOException e) { + log.log(Level.WARNING, "IOException encountered when sending response.", e); + } + } + + private static String decodeContent(Headers headers, InputStream inputStream) throws IOException { + List contentEncoding = headers.get("Content-encoding"); + InputStream input = inputStream; + try { + if (contentEncoding != null && !contentEncoding.isEmpty()) { + String encoding = contentEncoding.get(0); + if (ENCODINGS.contains(encoding)) { + input = new GZIPInputStream(inputStream); + } else if (!"identity".equals(encoding)) { + throw new IOException( + "The request has the following unsupported HTTP content encoding: " + encoding); + } + } + return new String(ByteStreams.toByteArray(input), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IOException("Exception encountered when decoding request content.", e); + } + } + + /** + * Generates a JSON response. + * + * @param context managedZones | projects | rrsets | changes + */ + @VisibleForTesting + static Response toListResponse(List serializedObjects, String context, String pageToken, + boolean includePageToken) { + StringBuilder responseBody = new StringBuilder(); + responseBody.append("{\"").append(context).append("\": ["); + Joiner.on(",").appendTo(responseBody, serializedObjects); + responseBody.append(']'); + // add page token only if it exists and is asked for + if (pageToken != null && includePageToken) { + responseBody.append(",\"nextPageToken\": \"").append(pageToken).append('"'); + } + responseBody.append('}'); + return new Response(HTTP_OK, responseBody.toString()); + } + + /** + * Prepares record sets that are created by default for each zone. + */ + private static ImmutableSortedMap defaultRecords(ManagedZone zone) { + ResourceRecordSet soa = new ResourceRecordSet(); + soa.setTtl(21600); + soa.setName(zone.getDnsName()); + soa.setRrdatas(ImmutableList.of( + // taken from the service + "ns-cloud-c1.googledomains.com. cloud-dns-hostmaster.google.com. 0 21600 3600 1209600 312" + )); + soa.setType("SOA"); + ResourceRecordSet ns = new ResourceRecordSet(); + ns.setTtl(21600); + ns.setName(zone.getDnsName()); + ns.setRrdatas(zone.getNameServers()); + ns.setType("NS"); + String nsId = getUniqueId(ImmutableSet.of()); + String soaId = getUniqueId(ImmutableSet.of(nsId)); + return ImmutableSortedMap.of(nsId, ns, soaId, soa); + } + + /** + * Returns a list of four nameservers randomly chosen from the predefined set. + */ + @VisibleForTesting + static List randomNameservers() { + ArrayList nameservers = Lists.newArrayList( + "dns1.googlecloud.com", "dns2.googlecloud.com", "dns3.googlecloud.com", + "dns4.googlecloud.com", "dns5.googlecloud.com", "dns6.googlecloud.com" + ); + while (nameservers.size() != 4) { + int index = ID_GENERATOR.nextInt(nameservers.size()); + nameservers.remove(index); + } + return nameservers; + } + + /** + * Returns a hex string id (used for a record set) unique within the set of ids. + */ + @VisibleForTesting + static String getUniqueId(Set ids) { + String id; + do { + id = Long.toHexString(System.currentTimeMillis()) + + Long.toHexString(Math.abs(ID_GENERATOR.nextLong())); + } while (ids.contains(id)); + return id; + } + + /** + * Tests if a record set matches name and type (if provided). Used for filtering. + */ + @VisibleForTesting + static boolean matchesCriteria(ResourceRecordSet recordSet, String name, String type) { + if (type != null && !recordSet.getType().equals(type)) { + return false; + } + return name == null || recordSet.getName().equals(name); + } + + /** + * Returns a project container. Never returns {@code null} because we assume that all projects + * exists. + */ + private ProjectContainer findProject(String projectId) { + ProjectContainer defaultProject = createProject(projectId); + projects.putIfAbsent(projectId, defaultProject); + return projects.get(projectId); + } + + /** + * Returns a zone container. Returns {@code null} if zone does not exist within project. + */ + @VisibleForTesting + ZoneContainer findZone(String projectId, String zoneName) { + ProjectContainer projectContainer = findProject(projectId); // never null + return projectContainer.zones().get(zoneName); + } + + /** + * Returns a change found by its id. Returns {@code null} if such a change does not exist. + */ + @VisibleForTesting + Change findChange(String projectId, String zoneName, String changeId) { + ZoneContainer wrapper = findZone(projectId, zoneName); + return wrapper == null ? null : wrapper.findChange(changeId); + } + + /** + * Returns a response to getChange service call. + */ + @VisibleForTesting + Response getChange(String projectId, String zoneName, String changeId, String query) { + Change change = findChange(projectId, zoneName, changeId); + if (change == null) { + ZoneContainer zone = findZone(projectId, zoneName); + if (zone == null) { + return Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named '%s' does not exist.", zoneName)); + } + return Error.NOT_FOUND.response(String.format( + "The 'parameters.changeId' resource named '%s' does not exist.", changeId)); + } + String[] fields = OptionParsers.parseGetOptions(query); + Change result = OptionParsers.extractFields(change, fields); + try { + return new Response(HTTP_OK, jsonFactory.toString(result)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response(String.format( + "Error when serializing change %s in managed zone %s in project %s.", + changeId, zoneName, projectId)); + } + } + + /** + * Returns a response to getZone service call. + */ + @VisibleForTesting + Response getZone(String projectId, String zoneName, String query) { + ZoneContainer container = findZone(projectId, zoneName); + if (container == null) { + return Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named '%s' does not exist.", zoneName)); + } + String[] fields = OptionParsers.parseGetOptions(query); + ManagedZone result = OptionParsers.extractFields(container.zone(), fields); + try { + return new Response(HTTP_OK, jsonFactory.toString(result)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response(String.format( + "Error when serializing managed zone %s in project %s.", zoneName, projectId)); + } + } + + /** + * We assume that every project exists. If we do not have it in the collection yet, we just create + * a new default project instance with default quota. + */ + @VisibleForTesting + Response getProject(String projectId, String query) { + String[] fields = OptionParsers.parseGetOptions(query); + Project project = findProject(projectId).project(); // creates project if needed + Project result = OptionParsers.extractFields(project, fields); + try { + return new Response(HTTP_OK, jsonFactory.toString(result)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response( + String.format("Error when serializing project %s.", projectId)); + } + } + + /** + * Creates a project. It generates a project number randomly. + */ + private ProjectContainer createProject(String projectId) { + Quota quota = new Quota(); + quota.setManagedZones(10000); + quota.setRrsetsPerManagedZone(10000); + quota.setRrsetAdditionsPerChange(100); + quota.setRrsetDeletionsPerChange(100); + quota.setTotalRrdataSizePerChange(10000); + quota.setResourceRecordsPerRrset(100); + Project project = new Project(); + project.setId(projectId); + project.setNumber(new BigInteger(String.valueOf( + Math.abs(ID_GENERATOR.nextLong() % Long.MAX_VALUE)))); + project.setQuota(quota); + return new ProjectContainer(project); + } + + @VisibleForTesting + Response deleteZone(String projectId, String zoneName) { + ZoneContainer zone = findZone(projectId, zoneName); + ImmutableSortedMap rrsets = zone == null + ? ImmutableSortedMap.of() : zone.dnsRecords().get(); + ImmutableList defaults = ImmutableList.of("NS", "SOA"); + for (ResourceRecordSet current : rrsets.values()) { + if (!defaults.contains(current.getType())) { + return Error.CONTAINER_NOT_EMPTY.response(String.format( + "The resource named '%s' cannot be deleted because it is not empty", zoneName)); + } + } + ProjectContainer projectContainer = projects.get(projectId); + ZoneContainer previous = projectContainer.zones.remove(zoneName); + return previous == null + ? Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named '%s' does not exist.", zoneName)) + : new Response(HTTP_NO_CONTENT, "{}"); + } + + /** + * Creates new managed zone and stores it in the collection. Assumes that project exists. + */ + @VisibleForTesting + Response createZone(String projectId, ManagedZone zone, String... fields) { + Response errorResponse = checkZone(zone); + if (errorResponse != null) { + return errorResponse; + } + ManagedZone completeZone = new ManagedZone(); + completeZone.setName(zone.getName()); + completeZone.setDnsName(zone.getDnsName()); + completeZone.setDescription(zone.getDescription()); + completeZone.setNameServerSet(zone.getNameServerSet()); + completeZone.setCreationTime(ISODateTimeFormat.dateTime().withZoneUTC() + .print(System.currentTimeMillis())); + completeZone.setId(BigInteger.valueOf(Math.abs(ID_GENERATOR.nextLong() % Long.MAX_VALUE))); + completeZone.setNameServers(randomNameservers()); + ZoneContainer zoneContainer = new ZoneContainer(completeZone); + ImmutableSortedMap defaultsRecords = defaultRecords(completeZone); + zoneContainer.dnsRecords().set(defaultsRecords); + Change change = new Change(); + change.setAdditions(ImmutableList.copyOf(defaultsRecords.values())); + change.setStatus("done"); + change.setId("0"); + change.setStartTime(ISODateTimeFormat.dateTime().withZoneUTC() + .print(System.currentTimeMillis())); + zoneContainer.changes().add(change); + ProjectContainer projectContainer = findProject(projectId); + ZoneContainer oldValue = projectContainer.zones().putIfAbsent( + completeZone.getName(), zoneContainer); + if (oldValue != null) { + return Error.ALREADY_EXISTS.response(String.format( + "The resource 'entity.managedZone' named '%s' already exists", completeZone.getName())); + } + ManagedZone result = OptionParsers.extractFields(completeZone, fields); + try { + return new Response(HTTP_OK, jsonFactory.toString(result)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response( + String.format("Error when serializing managed zone %s.", result.getName())); + } + } + + /** + * Creates a new change, stores it, and if delayChange > 0, invokes processing in a new thread. + */ + Response createChange(String projectId, String zoneName, Change change, String... fields) { + ZoneContainer zoneContainer = findZone(projectId, zoneName); + if (zoneContainer == null) { + return Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named %s does not exist.", zoneName)); + } + Response response = checkChange(change, zoneContainer); + if (response != null) { + return response; + } + Change completeChange = new Change(); + if (change.getAdditions() != null) { + completeChange.setAdditions(ImmutableList.copyOf(change.getAdditions())); + } + if (change.getDeletions() != null) { + completeChange.setDeletions(ImmutableList.copyOf(change.getDeletions())); + } + /* We need to set ID for the change. We are working in concurrent environment. We know that the + element fell on an index between 1 and maxId (index 0 is the default change which creates SOA + and NS), so we will reset all IDs between 0 and maxId (all of them are valid for the respective + objects). */ + ConcurrentLinkedQueue changeSequence = zoneContainer.changes(); + changeSequence.add(completeChange); + int maxId = changeSequence.size(); + int index = 0; + for (Change c : changeSequence) { + if (index == maxId) { + break; + } + c.setId(String.valueOf(index++)); + } + completeChange.setStatus("pending"); + completeChange.setStartTime(ISODateTimeFormat.dateTime().withZoneUTC() + .print(System.currentTimeMillis())); + invokeChange(projectId, zoneName, completeChange.getId()); + Change result = OptionParsers.extractFields(completeChange, fields); + try { + return new Response(HTTP_OK, jsonFactory.toString(result)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response( + String.format("Error when serializing change %s in managed zone %s in project %s.", + result.getId(), zoneName, projectId)); + } + } + + /** + * Applies change. Uses a different pooled thread which applies the change only if {@code + * delayChange} is > 0. + */ + private void invokeChange(final String projectId, final String zoneName, + final String changeId) { + if (delayChange > 0) { + EXECUTORS.schedule(new Runnable() { + @Override + public void run() { + applyExistingChange(projectId, zoneName, changeId); + } + }, delayChange, TimeUnit.MILLISECONDS); + } else { + applyExistingChange(projectId, zoneName, changeId); + } + } + + /** + * Applies changes to a zone. Repeatedly tries until succeeds. Thread safe and deadlock safe. + */ + private void applyExistingChange(String projectId, String zoneName, String changeId) { + Change change = findChange(projectId, zoneName, changeId); + if (change == null) { + return; // no such change exists, nothing to do + } + ZoneContainer wrapper = findZone(projectId, zoneName); + if (wrapper == null) { + return; // no such zone exists; it might have been deleted by another thread + } + AtomicReference> dnsRecords = + wrapper.dnsRecords(); + while (true) { + // managed zone must have a set of records which is not null + ImmutableSortedMap original = dnsRecords.get(); + // the copy will be populated when handling deletions + SortedMap copy = new TreeMap<>(); + // apply deletions first + List deletions = change.getDeletions(); + if (deletions != null) { + for (Map.Entry entry : original.entrySet()) { + if (!deletions.contains(entry.getValue())) { + copy.put(entry.getKey(), entry.getValue()); + } + } + } else { + copy.putAll(original); + } + // apply additions + List additions = change.getAdditions(); + if (additions != null) { + for (ResourceRecordSet addition : additions) { + ResourceRecordSet rrset = new ResourceRecordSet(); + rrset.setName(addition.getName()); + rrset.setRrdatas(ImmutableList.copyOf(addition.getRrdatas())); + rrset.setTtl(addition.getTtl()); + rrset.setType(addition.getType()); + String id = getUniqueId(copy.keySet()); + copy.put(id, rrset); + } + } + boolean success = dnsRecords.compareAndSet(original, ImmutableSortedMap.copyOf(copy)); + if (success) { + break; // success if no other thread modified the value in the meantime + } + } + change.setStatus("done"); + } + + /** + * Lists zones. Next page token is the last listed zone name and is returned only of there is more + * to list and if the user does not exclude nextPageToken from field options. + */ + @VisibleForTesting + Response listZones(String projectId, String query) { + Map options = OptionParsers.parseListZonesOptions(query); + Response response = checkListOptions(options); + if (response != null) { + return response; + } + ConcurrentSkipListMap containers = findProject(projectId).zones(); + String[] fields = (String[]) options.get("fields"); + String dnsName = (String) options.get("dnsName"); + String pageToken = (String) options.get("pageToken"); + Integer maxResults = options.get("maxResults") == null + ? null : Integer.valueOf((String) options.get("maxResults")); + boolean sizeReached = false; + boolean hasMorePages = false; + LinkedList serializedZones = new LinkedList<>(); + String lastZoneName = null; + ConcurrentNavigableMap fragment = + pageToken != null ? containers.tailMap(pageToken, false) : containers; + for (ZoneContainer zoneContainer : fragment.values()) { + ManagedZone zone = zoneContainer.zone(); + if (dnsName == null || zone.getDnsName().equals(dnsName)) { + if (sizeReached) { + // we do not add this, just note that there would be more and there should be a token + hasMorePages = true; + break; + } else { + try { + lastZoneName = zone.getName(); + serializedZones.addLast(jsonFactory.toString( + OptionParsers.extractFields(zone, fields))); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response(String.format( + "Error when serializing managed zone %s in project %s", lastZoneName, projectId)); + } + } + } + sizeReached = maxResults != null && maxResults.equals(serializedZones.size()); + } + boolean includePageToken = + hasMorePages && (fields == null || Arrays.asList(fields).contains("nextPageToken")); + return toListResponse(serializedZones, "managedZones", lastZoneName, includePageToken); + } + + /** + * Lists record sets for a zone. Next page token is the ID of the last record listed. + */ + @VisibleForTesting + Response listDnsRecords(String projectId, String zoneName, String query) { + Map options = OptionParsers.parseListDnsRecordsOptions(query); + Response response = checkListOptions(options); + if (response != null) { + return response; + } + ZoneContainer zoneContainer = findZone(projectId, zoneName); + if (zoneContainer == null) { + return Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named '%s' does not exist.", zoneName)); + } + ImmutableSortedMap dnsRecords = zoneContainer.dnsRecords().get(); + String[] fields = (String[]) options.get("fields"); + String name = (String) options.get("name"); + String type = (String) options.get("type"); + String pageToken = (String) options.get("pageToken"); + ImmutableSortedMap fragment = + pageToken != null ? dnsRecords.tailMap(pageToken, false) : dnsRecords; + Integer maxResults = options.get("maxResults") == null + ? null : Integer.valueOf((String) options.get("maxResults")); + boolean sizeReached = false; + boolean hasMorePages = false; + LinkedList serializedRrsets = new LinkedList<>(); + String lastRecordId = null; + for (String recordSetId : fragment.keySet()) { + ResourceRecordSet recordSet = fragment.get(recordSetId); + if (matchesCriteria(recordSet, name, type)) { + if (sizeReached) { + // we do not add this, just note that there would be more and there should be a token + hasMorePages = true; + break; + } else { + lastRecordId = recordSetId; + try { + serializedRrsets.addLast(jsonFactory.toString( + OptionParsers.extractFields(recordSet, fields))); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response(String.format( + "Error when serializing resource record set in managed zone %s in project %s", + zoneName, projectId)); + } + } + } + sizeReached = maxResults != null && maxResults.equals(serializedRrsets.size()); + } + boolean includePageToken = + hasMorePages && (fields == null || Arrays.asList(fields).contains("nextPageToken")); + return toListResponse(serializedRrsets, "rrsets", lastRecordId, includePageToken); + } + + /** + * Lists changes. Next page token is the ID of the last change listed. + */ + @VisibleForTesting + Response listChanges(String projectId, String zoneName, String query) { + Map options = OptionParsers.parseListChangesOptions(query); + Response response = checkListOptions(options); + if (response != null) { + return response; + } + ZoneContainer zoneContainer = findZone(projectId, zoneName); + if (zoneContainer == null) { + return Error.NOT_FOUND.response(String.format( + "The 'parameters.managedZone' resource named '%s' does not exist", zoneName)); + } + // take a sorted snapshot of the current change list + NavigableMap changes = new TreeMap<>(); + for (Change c : zoneContainer.changes()) { + if (c.getId() != null) { + changes.put(Integer.valueOf(c.getId()), c); + } + } + String[] fields = (String[]) options.get("fields"); + String sortOrder = (String) options.get("sortOrder"); + String pageToken = (String) options.get("pageToken"); + Integer maxResults = options.get("maxResults") == null + ? null : Integer.valueOf((String) options.get("maxResults")); + // as the only supported field is change sequence, we are not reading sortBy + NavigableSet keys; + if ("descending".equals(sortOrder)) { + keys = changes.descendingKeySet(); + } else { + keys = changes.navigableKeySet(); + } + Integer from = null; + try { + from = Integer.valueOf(pageToken); + } catch (NumberFormatException ex) { + // ignore page token + } + keys = from != null ? keys.tailSet(from, false) : keys; + NavigableMap fragment = + from != null && changes.containsKey(from) ? changes.tailMap(from, false) : changes; + boolean sizeReached = false; + boolean hasMorePages = false; + LinkedList serializedResults = new LinkedList<>(); + String lastChangeId = null; + for (Integer key : keys) { + Change change = fragment.get(key); + if (sizeReached) { + // we do not add this, just note that there would be more and there should be a token + hasMorePages = true; + break; + } else { + lastChangeId = change.getId(); + try { + serializedResults.addLast(jsonFactory.toString( + OptionParsers.extractFields(change, fields))); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response(String.format( + "Error when serializing change %s in managed zone %s in project %s", + lastChangeId, zoneName, projectId)); + } + } + sizeReached = maxResults != null && maxResults.equals(serializedResults.size()); + } + boolean includePageToken = + hasMorePages && (fields == null || Arrays.asList(fields).contains("nextPageToken")); + return toListResponse(serializedResults, "changes", lastChangeId, includePageToken); + } + + /** + * Validates a zone to be created. + */ + private static Response checkZone(ManagedZone zone) { + if (zone.getName() == null) { + return Error.REQUIRED.response( + "The 'entity.managedZone.name' parameter is required but was missing."); + } + if (zone.getDnsName() == null) { + return Error.REQUIRED.response( + "The 'entity.managedZone.dnsName' parameter is required but was missing."); + } + if (zone.getDescription() == null) { + return Error.REQUIRED.response( + "The 'entity.managedZone.description' parameter is required but was missing."); + } + try { + int number = Integer.valueOf(zone.getName()); + return Error.INVALID.response( + String.format("Invalid value for 'entity.managedZone.name': '%s'", number)); + } catch (NumberFormatException ex) { + // expected + } + if (zone.getName().isEmpty() || zone.getName().length() > 32 + || !ZONE_NAME_RE.matcher(zone.getName()).matches()) { + return Error.INVALID.response( + String.format("Invalid value for 'entity.managedZone.name': '%s'", zone.getName())); + } + if (zone.getDnsName().isEmpty() || !zone.getDnsName().endsWith(".")) { + return Error.INVALID.response( + String.format("Invalid value for 'entity.managedZone.dnsName': '%s'", zone.getDnsName())); + } + if (FORBIDDEN.contains(zone.getDnsName())) { + return Error.NOT_AVAILABLE.response(String.format( + "The '%s' managed zone is not available to be created.", zone.getDnsName())); + } + return null; + } + + /** + * Validates a change to be created. + */ + @VisibleForTesting + static Response checkChange(Change change, ZoneContainer zone) { + if ((change.getDeletions() == null || change.getDeletions().size() <= 0) + && (change.getAdditions() == null || change.getAdditions().size() <= 0)) { + return Error.REQUIRED.response("The 'entity.change' parameter is required but was missing."); + } + if (change.getAdditions() != null) { + int counter = 0; + for (ResourceRecordSet addition : change.getAdditions()) { + Response response = checkRrset(addition, zone, counter, "additions"); + if (response != null) { + return response; + } + counter++; + } + } + if (change.getDeletions() != null) { + int counter = 0; + for (ResourceRecordSet deletion : change.getDeletions()) { + Response response = checkRrset(deletion, zone, counter, "deletions"); + if (response != null) { + return response; + } + counter++; + } + } + return checkAdditionsDeletions(change.getAdditions(), change.getDeletions(), zone); + // null if everything is ok + } + + /** + * Checks a rrset within a change. + * + * @param type [additions|deletions] + * @param index the index or addition or deletion in the list + * @param zone the zone that this change is applied to + */ + @VisibleForTesting + static Response checkRrset(ResourceRecordSet rrset, ZoneContainer zone, int index, String type) { + if (rrset.getName() == null || !rrset.getName().endsWith(zone.zone().getDnsName())) { + return Error.INVALID.response(String.format( + "Invalid value for 'entity.change.%s[%s].name': '%s'", type, index, rrset.getName())); + } + if (rrset.getType() == null || !TYPES.contains(rrset.getType())) { + return Error.INVALID.response(String.format( + "Invalid value for 'entity.change.%s[%s].type': '%s'", type, index, rrset.getType())); + } + if (rrset.getTtl() != null && rrset.getTtl() != 0 && rrset.getTtl() < 0) { + return Error.INVALID.response(String.format( + "Invalid value for 'entity.change.%s[%s].ttl': '%s'", type, index, rrset.getTtl())); + } + if (rrset.getRrdatas() == null || rrset.isEmpty()) { + return Error.INVALID.response(String.format( + "Invalid value for 'entity.change.%s[%s].rrdata': '%s'", type, index, "")); + } + int counter = 0; + for (String record : rrset.getRrdatas()) { + if (!checkRrData(record, rrset.getType())) { + return Error.INVALID.response(String.format( + "Invalid value for 'entity.change.%s[%s].rrdata[%s]': '%s'", + type, index, counter, record)); + } + counter++; + } + if ("deletions".equals(type)) { + // check that deletion has a match by name and type + boolean found = false; + for (ResourceRecordSet wrappedRrset : zone.dnsRecords().get().values()) { + if (rrset.getName().equals(wrappedRrset.getName()) + && rrset.getType().equals(wrappedRrset.getType())) { + found = true; + break; + } + } + if (!found) { + return Error.NOT_FOUND.response(String.format( + "The 'entity.change.deletions[%s]' resource named '%s (%s)' does not exist.", + index, rrset.getName(), rrset.getType())); + } + // if found, we still need an exact match + if ("deletions".equals(type) + && !zone.dnsRecords().get().containsValue(rrset)) { + // such a record does not exist + return Error.CONDITION_NOT_MET.response(String.format( + "Precondition not met for 'entity.change.deletions[%s]", index)); + } + } + return null; + } + + /** + * Checks against duplicate additions (for each record set to be added that already exists, we + * must have a matching deletion. Furthermore, check that mandatory SOA and NS records stay. + */ + static Response checkAdditionsDeletions(List additions, + List deletions, ZoneContainer zone) { + if (additions != null) { + int index = 0; + for (ResourceRecordSet rrset : additions) { + for (ResourceRecordSet wrappedRrset : zone.dnsRecords().get().values()) { + if (rrset.getName().equals(wrappedRrset.getName()) + && rrset.getType().equals(wrappedRrset.getType()) + // such a record set exists and we must have a deletion + && (deletions == null || !deletions.contains(wrappedRrset))) { + return Error.ALREADY_EXISTS.response(String.format( + "The 'entity.change.additions[%s]' resource named '%s (%s)' already exists.", + index, rrset.getName(), rrset.getType())); + } + } + if (rrset.getType().equals("SOA") && findByNameAndType(deletions, null, "SOA") == null) { + return Error.INVALID_ZONE_APEX.response(String.format("The resource record set 'entity" + + ".change.additions[%s]' is invalid because a zone must contain exactly one resource" + + " record set of type 'SOA' at the apex.", index)); + } + if (rrset.getType().equals("NS") && findByNameAndType(deletions, null, "NS") == null) { + return Error.INVALID_ZONE_APEX.response(String.format("The resource record set 'entity" + + ".change.additions[%s]' is invalid because a zone must contain exactly one resource" + + " record set of type 'NS' at the apex.", index)); + } + index++; + } + } + if (deletions != null) { + int index = 0; + for (ResourceRecordSet rrset : deletions) { + if (rrset.getType().equals("SOA") && findByNameAndType(additions, null, "SOA") == null) { + return Error.INVALID_ZONE_APEX.response(String.format("The resource record set 'entity" + + ".change.deletions[%s]' is invalid because a zone must contain exactly one resource" + + " record set of type 'SOA' at the apex.", index)); + } + if (rrset.getType().equals("NS") && findByNameAndType(additions, null, "NS") == null) { + return Error.INVALID_ZONE_APEX.response(String.format("The resource record set 'entity" + + ".change.deletions[%s]' is invalid because a zone must contain exactly one resource" + + " record set of type 'NS' at the apex.", index)); + } + index++; + } + } + return null; + } + + /** + * Helper for searching rrsets in a collection. + */ + private static ResourceRecordSet findByNameAndType(Iterable recordSets, + String name, String type) { + if (recordSets != null) { + for (ResourceRecordSet rrset : recordSets) { + if ((name == null || name.equals(rrset.getName())) + && (type == null || type.equals(rrset.getType()))) { + return rrset; + } + } + } + return null; + } + + /** + * We only provide the most basic validation for A and AAAA record sets. + */ + static boolean checkRrData(String data, String type) { + switch (type) { + case "A": + return !data.contains(":") && isInetAddress(data); + case "AAAA": + return data.contains(":") && isInetAddress(data); + default: + return true; + } + } + + /** + * Check supplied listing options. + */ + @VisibleForTesting + static Response checkListOptions(Map options) { + // for general listing + String maxResultsString = (String) options.get("maxResults"); + if (maxResultsString != null) { + Integer maxResults; + try { + maxResults = Integer.valueOf(maxResultsString); + } catch (NumberFormatException ex) { + return Error.INVALID.response(String.format( + "Invalid integer value': '%s'.", maxResultsString)); + } + if (maxResults <= 0) { + return Error.INVALID.response(String.format( + "Invalid value for 'parameters.maxResults': '%s'", maxResults)); + } + } + String dnsName = (String) options.get("dnsName"); + if (dnsName != null && !dnsName.endsWith(".")) { + return Error.INVALID.response(String.format( + "Invalid value for 'parameters.dnsName': '%s'", dnsName)); + } + // for listing record sets, name must be fully qualified + String name = (String) options.get("name"); + if (name != null && !name.endsWith(".")) { + return Error.INVALID.response(String.format( + "Invalid value for 'parameters.name': '%s'", name)); + } + String type = (String) options.get("type"); // must be provided with name + if (type != null) { + if (name == null) { + return Error.INVALID.response("Invalid value for 'parameters.name': '' " + + "(name must be specified if type is specified)"); + } + if (!TYPES.contains(type)) { + return Error.INVALID.response(String.format( + "Invalid value for 'parameters.type': '%s'", type)); + } + } + // for listing changes + String order = (String) options.get("sortOrder"); + if (order != null && !"ascending".equals(order) && !"descending".equals(order)) { + return Error.INVALID.response(String.format( + "Invalid value for 'parameters.sortOrder': '%s'", order)); + } + String sortBy = (String) options.get("sortBy"); // case insensitive + if (sortBy != null && !"changesequence".equals(sortBy.toLowerCase())) { + return Error.INVALID.response(String.format( + "Invalid string value: '%s'. Allowed values: [changesequence]", sortBy)); + } + return null; + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/OptionParsers.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/OptionParsers.java new file mode 100644 index 000000000000..0543c6faab52 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/OptionParsers.java @@ -0,0 +1,258 @@ +/* + * Copyright 2016 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.cloud.dns.testing; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility helpers for LocalDnsHelper. + */ +class OptionParsers { + + private OptionParsers() { + } + + static Map parseListZonesOptions(String query) { + Map options = new HashMap<>(); + if (query != null) { + String[] args = query.split("&"); + for (String arg : args) { + String[] argEntry = arg.split("="); + switch (argEntry[0]) { + case "fields": + // List fields are in the form "managedZones(field1, field2, ...),nextPageToken" + String replaced = argEntry[1].replace("managedZones(", ","); + replaced = replaced.replace(")", ","); + // we will get empty strings, but it does not matter, they will be ignored + options.put("fields", replaced.split(",")); + break; + case "dnsName": + options.put("dnsName", argEntry[1]); + break; + case "pageToken": + options.put("pageToken", argEntry[1]); + break; + case "maxResults": + // parsing to int is done while handling + options.put("maxResults", argEntry[1]); + break; + default: + break; + } + } + } + return options; + } + + static String[] parseGetOptions(String query) { + if (query != null) { + String[] args = query.split("&"); + for (String arg : args) { + String[] argEntry = arg.split("="); + if (argEntry[0].equals("fields")) { + // List fields are in the form "fields=field1, field2,..." + return argEntry[1].split(","); + } + } + } + return new String[0]; + } + + static ManagedZone extractFields(ManagedZone fullZone, String... fields) { + if (fields == null || fields.length == 0) { + return fullZone; + } + ManagedZone managedZone = new ManagedZone(); + for (String field : fields) { + switch (field) { + case "creationTime": + managedZone.setCreationTime(fullZone.getCreationTime()); + break; + case "description": + managedZone.setDescription(fullZone.getDescription()); + break; + case "dnsName": + managedZone.setDnsName(fullZone.getDnsName()); + break; + case "id": + managedZone.setId(fullZone.getId()); + break; + case "name": + managedZone.setName(fullZone.getName()); + break; + case "nameServerSet": + managedZone.setNameServerSet(fullZone.getNameServerSet()); + break; + case "nameServers": + managedZone.setNameServers(fullZone.getNameServers()); + break; + default: + break; + } + } + return managedZone; + } + + static Change extractFields(Change fullChange, String... fields) { + if (fields == null || fields.length == 0) { + return fullChange; + } + Change change = new Change(); + for (String field : fields) { + switch (field) { + case "additions": + change.setAdditions(fullChange.getAdditions()); + break; + case "deletions": + change.setDeletions(fullChange.getDeletions()); + break; + case "id": + change.setId(fullChange.getId()); + break; + case "startTime": + change.setStartTime(fullChange.getStartTime()); + break; + case "status": + change.setStatus(fullChange.getStatus()); + break; + default: + break; + } + } + return change; + } + + static Project extractFields(Project fullProject, String... fields) { + if (fields == null || fields.length == 0) { + return fullProject; + } + Project project = new Project(); + for (String field : fields) { + switch (field) { + case "id": + project.setId(fullProject.getId()); + break; + case "number": + project.setNumber(fullProject.getNumber()); + break; + case "quota": + project.setQuota(fullProject.getQuota()); + break; + default: + break; + } + } + return project; + } + + static ResourceRecordSet extractFields(ResourceRecordSet fullRecord, String... fields) { + if (fields == null || fields.length == 0) { + return fullRecord; + } + ResourceRecordSet recordSet = new ResourceRecordSet(); + for (String field : fields) { + switch (field) { + case "name": + recordSet.setName(fullRecord.getName()); + break; + case "rrdatas": + recordSet.setRrdatas(fullRecord.getRrdatas()); + break; + case "type": + recordSet.setType(fullRecord.getType()); + break; + case "ttl": + recordSet.setTtl(fullRecord.getTtl()); + break; + default: + break; + } + } + return recordSet; + } + + static Map parseListChangesOptions(String query) { + Map options = new HashMap<>(); + if (query != null) { + String[] args = query.split("&"); + for (String arg : args) { + String[] argEntry = arg.split("="); + switch (argEntry[0]) { + case "fields": + String replaced = argEntry[1].replace("changes(", ",").replace(")", ","); + options.put("fields", replaced.split(",")); // empty strings will be ignored + break; + case "pageToken": + options.put("pageToken", argEntry[1]); + break; + case "sortBy": + options.put("sortBy", argEntry[1]); + break; + case "sortOrder": + options.put("sortOrder", argEntry[1]); + break; + case "maxResults": + // parsing to int is done while handling + options.put("maxResults", argEntry[1]); + break; + default: + break; + } + } + } + return options; + } + + static Map parseListDnsRecordsOptions(String query) { + Map options = new HashMap<>(); + if (query != null) { + String[] args = query.split("&"); + for (String arg : args) { + String[] argEntry = arg.split("="); + switch (argEntry[0]) { + case "fields": + String replace = argEntry[1].replace("rrsets(", ","); + replace = replace.replace(")", ","); + options.put("fields", replace.split(",")); // empty strings do not matter + break; + case "name": + options.put("name", argEntry[1]); + break; + case "type": + options.put("type", argEntry[1]); + break; + case "pageToken": + options.put("pageToken", argEntry[1]); + break; + case "maxResults": + // parsing to int is done while handling + options.put("maxResults", argEntry[1]); + break; + default: + break; + } + } + } + return options; + } +} diff --git a/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/package-info.java b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/package-info.java new file mode 100644 index 000000000000..da431658c712 --- /dev/null +++ b/gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/package-info.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 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. + */ + +/** + * A testing helper for Google Cloud DNS. + * + *

A simple usage example: + * Before the test: + *

 {@code
+ * // Minimum delay before processing change requests (in ms). Setting the delay to 0 makes change
+ * // request processing synchronous.
+ * long delay = 0;
+ * LocalDnsHelper dnsHelper = LocalDnsHelper.create(delay);
+ * dnsHelper.start();
+ * Dns dns = dnsHelper.options().service();
+ * }
+ * + *

After the test: + *

 {@code
+ * dnsHelper.stop();
+ * }
+ */ +package com.google.cloud.dns.testing; diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestInfoTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestInfoTest.java new file mode 100644 index 000000000000..2da7ad75c569 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestInfoTest.java @@ -0,0 +1,219 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.api.services.dns.model.Change; +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.util.List; + +public class ChangeRequestInfoTest { + + private static final String GENERATED_ID = "cr-id-1"; + private static final Long START_TIME_MILLIS = 12334567890L; + private static final ChangeRequest.Status STATUS = ChangeRequest.Status.PENDING; + private static final String NAME1 = "dns1"; + private static final RecordSet.Type TYPE1 = RecordSet.Type.A; + private static final String NAME2 = "dns2"; + private static final RecordSet.Type TYPE2 = RecordSet.Type.AAAA; + private static final String NAME3 = "dns3"; + private static final RecordSet.Type TYPE3 = RecordSet.Type.MX; + private static final RecordSet RECORD1 = RecordSet.builder(NAME1, TYPE1).build(); + private static final RecordSet RECORD2 = RecordSet.builder(NAME2, TYPE2).build(); + private static final RecordSet RECORD3 = RecordSet.builder(NAME3, TYPE3).build(); + private static final List ADDITIONS = ImmutableList.of(RECORD1, RECORD2); + private static final List DELETIONS = ImmutableList.of(RECORD3); + private static final ChangeRequestInfo CHANGE = ChangeRequest.builder() + .add(RECORD1) + .add(RECORD2) + .delete(RECORD3) + .startTimeMillis(START_TIME_MILLIS) + .status(STATUS) + .generatedId(GENERATED_ID) + .build(); + + @Test + public void testEmptyBuilder() { + ChangeRequestInfo cr = ChangeRequest.builder().build(); + assertNotNull(cr.deletions()); + assertTrue(cr.deletions().isEmpty()); + assertNotNull(cr.additions()); + assertTrue(cr.additions().isEmpty()); + } + + @Test + public void testBuilder() { + assertEquals(GENERATED_ID, CHANGE.generatedId()); + assertEquals(STATUS, CHANGE.status()); + assertEquals(START_TIME_MILLIS, CHANGE.startTimeMillis()); + assertEquals(ADDITIONS, CHANGE.additions()); + assertEquals(DELETIONS, CHANGE.deletions()); + List recordList = ImmutableList.of(RECORD1); + ChangeRequestInfo another = CHANGE.toBuilder().additions(recordList).build(); + assertEquals(recordList, another.additions()); + assertEquals(CHANGE.deletions(), another.deletions()); + another = CHANGE.toBuilder().deletions(recordList).build(); + assertEquals(recordList, another.deletions()); + assertEquals(CHANGE.additions(), another.additions()); + } + + @Test + public void testEqualsAndNotEquals() { + ChangeRequestInfo clone = CHANGE.toBuilder().build(); + assertEquals(CHANGE, clone); + clone = ChangeRequest.fromPb(CHANGE.toPb()); + assertEquals(CHANGE, clone); + clone = CHANGE.toBuilder().generatedId("some-other-id").build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().startTimeMillis(CHANGE.startTimeMillis() + 1).build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().add(RECORD3).build(); + assertNotEquals(CHANGE, clone); + clone = CHANGE.toBuilder().delete(RECORD1).build(); + assertNotEquals(CHANGE, clone); + ChangeRequestInfo empty = ChangeRequest.builder().build(); + assertNotEquals(CHANGE, empty); + assertEquals(empty, ChangeRequest.builder().build()); + } + + @Test + public void testSameHashCodeOnEquals() { + ChangeRequestInfo clone = CHANGE.toBuilder().build(); + assertEquals(CHANGE, clone); + assertEquals(CHANGE.hashCode(), clone.hashCode()); + ChangeRequestInfo empty = ChangeRequest.builder().build(); + assertEquals(empty.hashCode(), ChangeRequest.builder().build().hashCode()); + } + + @Test + public void testToAndFromPb() { + assertEquals(CHANGE, ChangeRequest.fromPb(CHANGE.toPb())); + ChangeRequestInfo partial = ChangeRequest.builder().build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().generatedId(GENERATED_ID).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().add(RECORD1).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().delete(RECORD1).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().additions(ADDITIONS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().deletions(DELETIONS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().startTimeMillis(START_TIME_MILLIS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + partial = ChangeRequest.builder().status(STATUS).build(); + assertEquals(partial, ChangeRequest.fromPb(partial.toPb())); + } + + @Test + public void testToBuilder() { + assertEquals(CHANGE, CHANGE.toBuilder().build()); + ChangeRequestInfo partial = ChangeRequest.builder().build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().generatedId(GENERATED_ID).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().add(RECORD1).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().delete(RECORD1).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().additions(ADDITIONS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().deletions(DELETIONS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().startTimeMillis(START_TIME_MILLIS).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ChangeRequest.builder().status(STATUS).build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void testClearAdditions() { + ChangeRequestInfo clone = CHANGE.toBuilder().clearAdditions().build(); + assertTrue(clone.additions().isEmpty()); + assertFalse(clone.deletions().isEmpty()); + } + + @Test + public void testAddAddition() { + try { + CHANGE.toBuilder().add(null); + fail("Should not be able to add null RecordSet."); + } catch (NullPointerException e) { + // expected + } + ChangeRequestInfo clone = CHANGE.toBuilder().add(RECORD1).build(); + assertEquals(CHANGE.additions().size() + 1, clone.additions().size()); + } + + @Test + public void testAddDeletion() { + try { + CHANGE.toBuilder().delete(null); + fail("Should not be able to delete null RecordSet."); + } catch (NullPointerException e) { + // expected + } + ChangeRequestInfo clone = CHANGE.toBuilder().delete(RECORD1).build(); + assertEquals(CHANGE.deletions().size() + 1, clone.deletions().size()); + } + + @Test + public void testClearDeletions() { + ChangeRequestInfo clone = CHANGE.toBuilder().clearDeletions().build(); + assertTrue(clone.deletions().isEmpty()); + assertFalse(clone.additions().isEmpty()); + } + + @Test + public void testRemoveAddition() { + ChangeRequestInfo clone = CHANGE.toBuilder().removeAddition(RECORD1).build(); + assertTrue(clone.additions().contains(RECORD2)); + assertFalse(clone.additions().contains(RECORD1)); + assertTrue(clone.deletions().contains(RECORD3)); + clone = CHANGE.toBuilder().removeAddition(RECORD2).removeAddition(RECORD1).build(); + assertFalse(clone.additions().contains(RECORD2)); + assertFalse(clone.additions().contains(RECORD1)); + assertTrue(clone.additions().isEmpty()); + assertTrue(clone.deletions().contains(RECORD3)); + } + + @Test + public void testRemoveDeletion() { + ChangeRequestInfo clone = CHANGE.toBuilder().removeDeletion(RECORD3).build(); + assertTrue(clone.deletions().isEmpty()); + } + + @Test + public void testDateParsing() { + String startTime = "2016-01-26T18:33:43.512Z"; // obtained from service + Change change = CHANGE.toPb().setStartTime(startTime); + ChangeRequestInfo converted = ChangeRequest.fromPb(change); + assertNotNull(converted.startTimeMillis()); + assertEquals(change, converted.toPb()); + assertEquals(change.getStartTime(), converted.toPb().getStartTime()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestTest.java new file mode 100644 index 000000000000..6daa1a0a8cdd --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ChangeRequestTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ChangeRequestTest { + + private static final String ZONE_NAME = "dns-zone-name"; + private static final ChangeRequestInfo CHANGE_REQUEST_INFO = ChangeRequest.builder() + .add(RecordSet.builder("name", RecordSet.Type.A).build()) + .delete(RecordSet.builder("othername", RecordSet.Type.AAAA).build()) + .build(); + private static final DnsOptions OPTIONS = createStrictMock(DnsOptions.class); + + private Dns dns; + private ChangeRequest changeRequest; + private ChangeRequest changeRequestPending; + private ChangeRequest changeRequestPartial; + + @Before + public void setUp() throws Exception { + dns = createStrictMock(Dns.class); + expect(dns.options()).andReturn(OPTIONS).times(3); + replay(dns); + changeRequest = new ChangeRequest(dns, ZONE_NAME, new ChangeRequestInfo.BuilderImpl( + CHANGE_REQUEST_INFO.toBuilder() + .startTimeMillis(132L) + .generatedId("12") + .status(ChangeRequest.Status.DONE) + .build())); + changeRequestPending = new ChangeRequest(dns, ZONE_NAME, new ChangeRequestInfo.BuilderImpl( + CHANGE_REQUEST_INFO.toBuilder() + .startTimeMillis(132L) + .generatedId("12") + .status(ChangeRequest.Status.PENDING) + .build())); + changeRequestPartial = new ChangeRequest(dns, ZONE_NAME, + new ChangeRequest.BuilderImpl(CHANGE_REQUEST_INFO)); + reset(dns); + } + + @After + public void tearDown() throws Exception { + verify(dns); + } + + @Test + public void testConstructor() { + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + assertEquals(new ChangeRequest(dns, ZONE_NAME, + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_INFO)), changeRequestPartial); + assertNotNull(changeRequest.dns()); + assertEquals(ZONE_NAME, changeRequest.zone()); + assertSame(dns, changeRequestPartial.dns()); + assertEquals(ZONE_NAME, changeRequestPartial.zone()); + } + + @Test + public void testFromPb() { + expect(dns.options()).andReturn(OPTIONS).times(2); + replay(dns); + assertEquals(changeRequest, ChangeRequest.fromPb(dns, ZONE_NAME, changeRequest.toPb())); + assertEquals(changeRequestPartial, + ChangeRequest.fromPb(dns, ZONE_NAME, changeRequestPartial.toPb())); + } + + @Test + public void testEqualsAndToBuilder() { + expect(dns.options()).andReturn(OPTIONS).times(2); + replay(dns); + ChangeRequest compare = changeRequest.toBuilder().build(); + assertEquals(changeRequest, compare); + assertEquals(changeRequest.hashCode(), compare.hashCode()); + compare = changeRequestPartial.toBuilder().build(); + assertEquals(changeRequestPartial, compare); + assertEquals(changeRequestPartial.hashCode(), compare.hashCode()); + } + + @Test + public void testBuilder() { + // one for each build() call because it invokes a constructor + expect(dns.options()).andReturn(OPTIONS).times(9); + replay(dns); + String id = changeRequest.generatedId() + "aaa"; + assertEquals(id, changeRequest.toBuilder().generatedId(id).build().generatedId()); + ChangeRequest modified = + changeRequest.toBuilder().status(ChangeRequest.Status.PENDING).build(); + assertEquals(ChangeRequest.Status.PENDING, modified.status()); + modified = changeRequest.toBuilder().clearDeletions().build(); + assertTrue(modified.deletions().isEmpty()); + modified = changeRequest.toBuilder().clearAdditions().build(); + assertTrue(modified.additions().isEmpty()); + modified = changeRequest.toBuilder().additions(ImmutableList.of()).build(); + assertTrue(modified.additions().isEmpty()); + modified = changeRequest.toBuilder().deletions(ImmutableList.of()).build(); + assertTrue(modified.deletions().isEmpty()); + RecordSet cname = RecordSet.builder("last", RecordSet.Type.CNAME).build(); + modified = changeRequest.toBuilder().add(cname).build(); + assertTrue(modified.additions().contains(cname)); + modified = changeRequest.toBuilder().delete(cname).build(); + assertTrue(modified.deletions().contains(cname)); + modified = changeRequest.toBuilder().startTimeMillis(0L).build(); + assertEquals(Long.valueOf(0), modified.startTimeMillis()); + } + + @Test + public void testApplyTo() { + expect(dns.applyChangeRequest(ZONE_NAME, changeRequest)).andReturn(changeRequest); + expect(dns.applyChangeRequest(ZONE_NAME, changeRequest, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME))) + .andReturn(changeRequest); + replay(dns); + assertSame(changeRequest, changeRequest.applyTo(ZONE_NAME)); + assertSame(changeRequest, changeRequest.applyTo(ZONE_NAME, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME))); + } + + @Test + public void testReload() { + expect(dns.getChangeRequest(ZONE_NAME, changeRequest.generatedId())).andReturn(changeRequest); + expect(dns.getChangeRequest(ZONE_NAME, changeRequest.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME))) + .andReturn(changeRequest); + replay(dns); + assertSame(changeRequest, changeRequest.reload()); + assertSame(changeRequest, changeRequest.reload( + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME))); + } + + @Test + public void testIsDone() { + replay(dns); + assertTrue(changeRequest.isDone()); + verify(dns); + reset(dns); + expect(dns.getChangeRequest(ZONE_NAME, changeRequest.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS))) + .andReturn(changeRequest); + replay(dns); + assertTrue(changeRequestPending.isDone()); + verify(dns); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsImplTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsImplTest.java new file mode 100644 index 000000000000..457f5ed3adb3 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsImplTest.java @@ -0,0 +1,389 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.cloud.Page; +import com.google.cloud.RetryParams; +import com.google.cloud.ServiceOptions; +import com.google.cloud.dns.spi.DnsRpc; +import com.google.cloud.dns.spi.DnsRpcFactory; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +public class DnsImplTest { + + // Dns entities + private static final String ZONE_NAME = "some zone name"; + private static final String DNS_NAME = "example.com."; + private static final String DESCRIPTION = "desc"; + private static final String CHANGE_ID = "some change id"; + private static final RecordSet DNS_RECORD1 = + RecordSet.builder("Something", RecordSet.Type.AAAA).build(); + private static final RecordSet DNS_RECORD2 = + RecordSet.builder("Different", RecordSet.Type.AAAA).build(); + private static final Integer MAX_SIZE = 20; + private static final String PAGE_TOKEN = "some token"; + private static final ZoneInfo ZONE_INFO = ZoneInfo.of(ZONE_NAME, DNS_NAME, DESCRIPTION); + private static final ProjectInfo PROJECT_INFO = ProjectInfo.builder().build(); + private static final ChangeRequestInfo CHANGE_REQUEST_PARTIAL = ChangeRequestInfo.builder() + .add(DNS_RECORD1) + .build(); + private static final ChangeRequestInfo CHANGE_REQUEST_COMPLETE = ChangeRequestInfo.builder() + .add(DNS_RECORD1) + .startTimeMillis(123L) + .status(ChangeRequest.Status.PENDING) + .generatedId(CHANGE_ID) + .build(); + + // Result lists + private static final DnsRpc.ListResult LIST_RESULT_OF_PB_CHANGES = + DnsRpc.ListResult.of("cursor", ImmutableList.of(CHANGE_REQUEST_COMPLETE.toPb(), + CHANGE_REQUEST_PARTIAL.toPb())); + private static final DnsRpc.ListResult LIST_RESULT_OF_PB_ZONES = + DnsRpc.ListResult.of("cursor", ImmutableList.of(ZONE_INFO.toPb())); + private static final DnsRpc.ListResult LIST_OF_PB_DNS_RECORDS = + DnsRpc.ListResult.of("cursor", ImmutableList.of(DNS_RECORD1.toPb(), DNS_RECORD2.toPb())); + + // Field options + private static final Dns.ZoneOption ZONE_FIELDS = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.ProjectOption PROJECT_FIELDS = + Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + private static final Dns.ChangeRequestOption CHANGE_GET_FIELDS = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS); + + // Listing options + private static final Dns.ZoneListOption[] ZONE_LIST_OPTIONS = { + Dns.ZoneListOption.pageSize(MAX_SIZE), Dns.ZoneListOption.pageToken(PAGE_TOKEN), + Dns.ZoneListOption.fields(Dns.ZoneField.DESCRIPTION), + Dns.ZoneListOption.dnsName(DNS_NAME)}; + private static final Dns.ChangeRequestListOption[] CHANGE_LIST_OPTIONS = { + Dns.ChangeRequestListOption.pageSize(MAX_SIZE), + Dns.ChangeRequestListOption.pageToken(PAGE_TOKEN), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.STATUS), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING)}; + private static final Dns.RecordSetListOption[] DNS_RECORD_LIST_OPTIONS = { + Dns.RecordSetListOption.pageSize(MAX_SIZE), + Dns.RecordSetListOption.pageToken(PAGE_TOKEN), + Dns.RecordSetListOption.fields(Dns.RecordSetField.TTL), + Dns.RecordSetListOption.dnsName(DNS_NAME), + Dns.RecordSetListOption.type(RecordSet.Type.AAAA)}; + + // Other + private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); + private static final ServiceOptions.Clock TIME_SOURCE = new ServiceOptions.Clock() { + @Override + public long millis() { + return 42000L; + } + }; + + private DnsOptions options; + private DnsRpcFactory rpcFactoryMock; + private DnsRpc dnsRpcMock; + private Dns dns; + + @Before + public void setUp() { + rpcFactoryMock = EasyMock.createMock(DnsRpcFactory.class); + dnsRpcMock = EasyMock.createMock(DnsRpc.class); + EasyMock.expect(rpcFactoryMock.create(EasyMock.anyObject(DnsOptions.class))) + .andReturn(dnsRpcMock); + EasyMock.replay(rpcFactoryMock); + options = DnsOptions.builder() + .projectId("projectId") + .clock(TIME_SOURCE) + .serviceRpcFactory(rpcFactoryMock) + .retryParams(RetryParams.noRetries()) + .build(); + } + + @After + public void tearDown() throws Exception { + EasyMock.verify(rpcFactoryMock); + } + + @Test + public void testCreateZone() { + EasyMock.expect(dnsRpcMock.create(ZONE_INFO.toPb(), EMPTY_RPC_OPTIONS)) + .andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.create(ZONE_INFO); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + } + + @Test + public void testCreateZoneWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.create(EasyMock.eq(ZONE_INFO.toPb()), + EasyMock.capture(capturedOptions))).andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.create(ZONE_INFO, ZONE_FIELDS); + String selector = (String) capturedOptions.getValue().get(ZONE_FIELDS.rpcOption()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + assertTrue(selector.contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + } + + @Test + public void testGetZone() { + EasyMock.expect(dnsRpcMock.getZone(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.getZone(ZONE_INFO.name()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + } + + @Test + public void testGetZoneWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getZone(EasyMock.eq(ZONE_INFO.name()), + EasyMock.capture(capturedOptions))).andReturn(ZONE_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Zone zone = dns.getZone(ZONE_INFO.name(), ZONE_FIELDS); + String selector = (String) capturedOptions.getValue().get(ZONE_FIELDS.rpcOption()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), zone); + assertTrue(selector.contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + } + + @Test + public void testDeleteZone() { + EasyMock.expect(dnsRpcMock.deleteZone(ZONE_INFO.name())) + .andReturn(true); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + assertTrue(dns.delete(ZONE_INFO.name())); + } + + @Test + public void testGetProject() { + EasyMock.expect(dnsRpcMock.getProject(EMPTY_RPC_OPTIONS)) + .andReturn(PROJECT_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ProjectInfo projectInfo = dns.getProject(); + assertEquals(PROJECT_INFO, projectInfo); + } + + @Test + public void testProjectGetWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getProject(EasyMock.capture(capturedOptions))) + .andReturn(PROJECT_INFO.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ProjectInfo projectInfo = dns.getProject(PROJECT_FIELDS); + String selector = (String) capturedOptions.getValue().get(PROJECT_FIELDS.rpcOption()); + assertEquals(PROJECT_INFO, projectInfo); + assertTrue(selector.contains(Dns.ProjectField.QUOTA.selector())); + assertTrue(selector.contains(Dns.ProjectField.PROJECT_ID.selector())); + } + + @Test + public void testGetChangeRequest() { + EasyMock.expect(dnsRpcMock.getChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_COMPLETE.generatedId(), EMPTY_RPC_OPTIONS)) + .andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.getChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_COMPLETE.generatedId()); + assertEquals(new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)), changeRequest); + } + + @Test + public void testGetChangeRequestWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.getChangeRequest(EasyMock.eq(ZONE_INFO.name()), + EasyMock.eq(CHANGE_REQUEST_COMPLETE.generatedId()), EasyMock.capture(capturedOptions))) + .andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.getChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_COMPLETE.generatedId(), CHANGE_GET_FIELDS); + String selector = (String) capturedOptions.getValue().get(CHANGE_GET_FIELDS.rpcOption()); + assertEquals(new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)), changeRequest); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + } + + @Test + public void testApplyChangeRequest() { + EasyMock.expect(dnsRpcMock.applyChangeRequest(ZONE_INFO.name(), CHANGE_REQUEST_PARTIAL.toPb(), + EMPTY_RPC_OPTIONS)).andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.applyChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_PARTIAL); + assertEquals(new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)), changeRequest); + } + + @Test + public void testApplyChangeRequestWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.applyChangeRequest(EasyMock.eq(ZONE_INFO.name()), + EasyMock.eq(CHANGE_REQUEST_PARTIAL.toPb()), EasyMock.capture(capturedOptions))) + .andReturn(CHANGE_REQUEST_COMPLETE.toPb()); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + ChangeRequest changeRequest = dns.applyChangeRequest(ZONE_INFO.name(), + CHANGE_REQUEST_PARTIAL, CHANGE_GET_FIELDS); + String selector = (String) capturedOptions.getValue().get(CHANGE_GET_FIELDS.rpcOption()); + assertEquals(new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)), changeRequest); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + } + + // lists + @Test + public void testListChangeRequests() { + EasyMock.expect(dnsRpcMock.listChangeRequests(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(LIST_RESULT_OF_PB_CHANGES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page changeRequestPage = dns.listChangeRequests(ZONE_INFO.name()); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains( + new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)))); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains( + new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_PARTIAL)))); + assertEquals(2, Lists.newArrayList(changeRequestPage.values()).size()); + } + + @Test + public void testListChangeRequestsWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listChangeRequests(EasyMock.eq(ZONE_NAME), + EasyMock.capture(capturedOptions))).andReturn(LIST_RESULT_OF_PB_CHANGES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page changeRequestPage = dns.listChangeRequests(ZONE_NAME, CHANGE_LIST_OPTIONS); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains( + new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_COMPLETE)))); + assertTrue(Lists.newArrayList(changeRequestPage.values()).contains( + new ChangeRequest(dns, ZONE_INFO.name(), + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_PARTIAL)))); + assertEquals(2, Lists.newArrayList(changeRequestPage.values()).size()); + Integer size = (Integer) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(selector.contains(Dns.ChangeRequestField.ID.selector())); + selector = (String) capturedOptions.getValue().get(CHANGE_LIST_OPTIONS[3].rpcOption()); + assertTrue(selector.contains(Dns.SortingOrder.ASCENDING.selector())); + } + + @Test + public void testListZones() { + EasyMock.expect(dnsRpcMock.listZones(EMPTY_RPC_OPTIONS)) + .andReturn(LIST_RESULT_OF_PB_ZONES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page zonePage = dns.listZones(); + assertEquals(1, Lists.newArrayList(zonePage.values()).size()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), + Lists.newArrayList(zonePage.values()).get(0)); + } + + @Test + public void testListZonesWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listZones(EasyMock.capture(capturedOptions))) + .andReturn(LIST_RESULT_OF_PB_ZONES); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page zonePage = dns.listZones(ZONE_LIST_OPTIONS); + assertEquals(1, Lists.newArrayList(zonePage.values()).size()); + assertEquals(new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)), + Lists.newArrayList(zonePage.values()).get(0)); + Integer size = (Integer) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.ZoneField.DESCRIPTION.selector())); + assertTrue(selector.contains(Dns.ZoneField.NAME.selector())); + selector = (String) capturedOptions.getValue().get(ZONE_LIST_OPTIONS[3].rpcOption()); + assertEquals(DNS_NAME, selector); + } + + @Test + public void testListDnsRecords() { + EasyMock.expect(dnsRpcMock.listRecordSets(ZONE_INFO.name(), EMPTY_RPC_OPTIONS)) + .andReturn(LIST_OF_PB_DNS_RECORDS); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page dnsPage = dns.listRecordSets(ZONE_INFO.name()); + assertEquals(2, Lists.newArrayList(dnsPage.values()).size()); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD1)); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD2)); + } + + @Test + public void testListDnsRecordsWithOptions() { + Capture> capturedOptions = Capture.newInstance(); + EasyMock.expect(dnsRpcMock.listRecordSets(EasyMock.eq(ZONE_NAME), + EasyMock.capture(capturedOptions))).andReturn(LIST_OF_PB_DNS_RECORDS); + EasyMock.replay(dnsRpcMock); + dns = options.service(); // creates DnsImpl + Page dnsPage = dns.listRecordSets(ZONE_NAME, DNS_RECORD_LIST_OPTIONS); + assertEquals(2, Lists.newArrayList(dnsPage.values()).size()); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD1)); + assertTrue(Lists.newArrayList(dnsPage.values()).contains(DNS_RECORD2)); + Integer size = (Integer) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[0].rpcOption()); + assertEquals(MAX_SIZE, size); + String selector = (String) capturedOptions.getValue() + .get(DNS_RECORD_LIST_OPTIONS[1].rpcOption()); + assertEquals(PAGE_TOKEN, selector); + selector = (String) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[2].rpcOption()); + assertTrue(selector.contains(Dns.RecordSetField.NAME.selector())); + assertTrue(selector.contains(Dns.RecordSetField.TTL.selector())); + selector = (String) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[3].rpcOption()); + assertEquals(DNS_RECORD_LIST_OPTIONS[3].value(), selector); + String type = (String) capturedOptions.getValue().get(DNS_RECORD_LIST_OPTIONS[4] + .rpcOption()); + assertEquals(DNS_RECORD_LIST_OPTIONS[4].value(), type); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsTest.java new file mode 100644 index 000000000000..91d46e94262d --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.dns.spi.DnsRpc; + +import org.junit.Test; + +public class DnsTest { + + private static final Integer PAGE_SIZE = 20; + private static final String PAGE_TOKEN = "page token"; + private static final String DNS_NAME = "www.example.com."; + + @Test + public void testRecordSetListOption() { + // dns name + String dnsName = "some name"; + Dns.RecordSetListOption recordSetListOption = Dns.RecordSetListOption.dnsName(dnsName); + assertEquals(dnsName, recordSetListOption.value()); + assertEquals(DnsRpc.Option.NAME, recordSetListOption.rpcOption()); + // page token + recordSetListOption = Dns.RecordSetListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, recordSetListOption.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, recordSetListOption.rpcOption()); + // page size + recordSetListOption = Dns.RecordSetListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, recordSetListOption.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, recordSetListOption.rpcOption()); + // record type + RecordSet.Type recordType = RecordSet.Type.AAAA; + recordSetListOption = Dns.RecordSetListOption.type(recordType); + assertEquals(recordType.name(), recordSetListOption.value()); + assertEquals(DnsRpc.Option.DNS_TYPE, recordSetListOption.rpcOption()); + // fields + recordSetListOption = Dns.RecordSetListOption.fields(Dns.RecordSetField.NAME, + Dns.RecordSetField.TTL); + assertEquals(DnsRpc.Option.FIELDS, recordSetListOption.rpcOption()); + assertTrue(recordSetListOption.value() instanceof String); + assertTrue(((String) recordSetListOption.value()).contains( + Dns.RecordSetField.NAME.selector())); + assertTrue(((String) recordSetListOption.value()).contains( + Dns.RecordSetField.TTL.selector())); + assertTrue(((String) recordSetListOption.value()).contains( + Dns.RecordSetField.NAME.selector())); + } + + @Test + public void testZoneOption() { + Dns.ZoneOption fields = Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME, + Dns.ZoneField.DESCRIPTION); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.DESCRIPTION.selector())); + } + + @Test + public void testZoneList() { + // fields + Dns.ZoneListOption fields = Dns.ZoneListOption.fields(Dns.ZoneField.CREATION_TIME, + Dns.ZoneField.DESCRIPTION); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.CREATION_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.DESCRIPTION.selector())); + assertTrue(((String) fields.value()).contains(Dns.ZoneField.NAME.selector())); + // page token + Dns.ZoneListOption option = Dns.ZoneListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, option.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, option.rpcOption()); + // page size + option = Dns.ZoneListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, option.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, option.rpcOption()); + // dnsName filter + option = Dns.ZoneListOption.dnsName(DNS_NAME); + assertEquals(DNS_NAME, option.value()); + assertEquals(DnsRpc.Option.DNS_NAME, option.rpcOption()); + } + + @Test + public void testProjectGetOption() { + // fields + Dns.ProjectOption fields = Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains(Dns.ProjectField.QUOTA.selector())); + assertTrue(((String) fields.value()).contains(Dns.ProjectField.PROJECT_ID.selector())); + } + + @Test + public void testChangeRequestOption() { + // fields + Dns.ChangeRequestOption fields = Dns.ChangeRequestOption.fields( + Dns.ChangeRequestField.START_TIME, Dns.ChangeRequestField.STATUS); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains( + Dns.ChangeRequestField.START_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.ID.selector())); + } + + @Test + public void testChangeRequestListOption() { + // fields + Dns.ChangeRequestListOption fields = Dns.ChangeRequestListOption.fields( + Dns.ChangeRequestField.START_TIME, Dns.ChangeRequestField.STATUS); + assertEquals(DnsRpc.Option.FIELDS, fields.rpcOption()); + assertTrue(fields.value() instanceof String); + assertTrue(((String) fields.value()).contains( + Dns.ChangeRequestField.START_TIME.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.STATUS.selector())); + assertTrue(((String) fields.value()).contains(Dns.ChangeRequestField.ID.selector())); + // page token + Dns.ChangeRequestListOption option = Dns.ChangeRequestListOption.pageToken(PAGE_TOKEN); + assertEquals(PAGE_TOKEN, option.value()); + assertEquals(DnsRpc.Option.PAGE_TOKEN, option.rpcOption()); + // page size + option = Dns.ChangeRequestListOption.pageSize(PAGE_SIZE); + assertEquals(PAGE_SIZE, option.value()); + assertEquals(DnsRpc.Option.PAGE_SIZE, option.rpcOption()); + // sort order + option = Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING); + assertEquals(DnsRpc.Option.SORTING_ORDER, option.rpcOption()); + assertEquals(Dns.SortingOrder.ASCENDING.selector(), option.value()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/OptionTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/OptionTest.java new file mode 100644 index 000000000000..1e5be1287ceb --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/OptionTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.dns.spi.DnsRpc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class OptionTest { + + private static final DnsRpc.Option RPC_OPTION = DnsRpc.Option.DNS_TYPE; + private static final DnsRpc.Option ANOTHER_RPC_OPTION = DnsRpc.Option.DNS_NAME; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final Option OPTION = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_EQUALS = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_NOT_EQUALS1 = new Option(RPC_OPTION, OTHER_VALUE) {}; + private static final Option OPTION_NOT_EQUALS2 = new Option(ANOTHER_RPC_OPTION, VALUE) {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(RPC_OPTION, OPTION.rpcOption()); + assertEquals(VALUE, OPTION.value()); + Option option = new Option(RPC_OPTION, null) {}; + assertEquals(RPC_OPTION, option.rpcOption()); + assertNull(option.value()); + thrown.expect(NullPointerException.class); + new Option(null, VALUE) {}; + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/ProjectInfoTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ProjectInfoTest.java new file mode 100644 index 000000000000..70086250e6dd --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ProjectInfoTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import java.math.BigInteger; + +public class ProjectInfoTest { + + private static final String ID = "project-id-123"; + private static final BigInteger NUMBER = new BigInteger("123"); + private static final ProjectInfo.Quota QUOTA = new ProjectInfo.Quota(1, 2, 3, 4, 5, 6); + private static final ProjectInfo PROJECT_INFO = ProjectInfo.builder() + .id(ID).number(NUMBER).quota(QUOTA).build(); + + @Test + public void testBuilder() { + ProjectInfo withId = ProjectInfo.builder().id(ID).build(); + assertEquals(ID, withId.id()); + assertNull(withId.number()); + assertNull(withId.quota()); + ProjectInfo withNumber = ProjectInfo.builder().number(NUMBER).build(); + assertEquals(NUMBER, withNumber.number()); + assertNull(withNumber.quota()); + assertNull(withNumber.id()); + ProjectInfo withQuota = ProjectInfo.builder().quota(QUOTA).build(); + assertEquals(QUOTA, withQuota.quota()); + assertNull(withQuota.id()); + assertNull(withQuota.number()); + assertEquals(QUOTA, PROJECT_INFO.quota()); + assertEquals(NUMBER, PROJECT_INFO.number()); + assertEquals(ID, PROJECT_INFO.id()); + } + + @Test + public void testQuotaConstructor() { + assertEquals(1, QUOTA.zones()); + assertEquals(2, QUOTA.resourceRecordsPerRrset()); + assertEquals(3, QUOTA.rrsetAdditionsPerChange()); + assertEquals(4, QUOTA.rrsetDeletionsPerChange()); + assertEquals(5, QUOTA.rrsetsPerZone()); + assertEquals(6, QUOTA.totalRrdataSizePerChange()); + } + + @Test + public void testEqualsAndNotEqualsQuota() { + ProjectInfo.Quota clone = new ProjectInfo.Quota(6, 5, 4, 3, 2, 1); + assertNotEquals(QUOTA, clone); + clone = ProjectInfo.Quota.fromPb(QUOTA.toPb()); + assertEquals(QUOTA, clone); + } + + @Test + public void testSameHashCodeOnEqualsQuota() { + ProjectInfo.Quota clone = ProjectInfo.Quota.fromPb(QUOTA.toPb()); + assertEquals(QUOTA, clone); + assertEquals(QUOTA.hashCode(), clone.hashCode()); + } + + @Test + public void testEqualsAndNotEquals() { + ProjectInfo clone = ProjectInfo.builder().build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().id(PROJECT_INFO.id()).number(PROJECT_INFO.number()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().id(PROJECT_INFO.id()).quota(PROJECT_INFO.quota()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.builder().number(PROJECT_INFO.number()).quota(PROJECT_INFO.quota()).build(); + assertNotEquals(PROJECT_INFO, clone); + clone = ProjectInfo.fromPb(PROJECT_INFO.toPb()); + assertEquals(PROJECT_INFO, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + ProjectInfo clone = ProjectInfo.fromPb(PROJECT_INFO.toPb()); + assertEquals(PROJECT_INFO, clone); + assertEquals(PROJECT_INFO.hashCode(), clone.hashCode()); + } + + @Test + public void testToAndFromPb() { + assertEquals(PROJECT_INFO, ProjectInfo.fromPb(PROJECT_INFO.toPb())); + ProjectInfo partial = ProjectInfo.builder().id(ID).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + partial = ProjectInfo.builder().number(NUMBER).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + partial = ProjectInfo.builder().quota(QUOTA).build(); + assertEquals(partial, ProjectInfo.fromPb(partial.toPb())); + assertNotEquals(PROJECT_INFO, partial); + } + + @Test + public void testToAndFromPbQuota() { + assertEquals(QUOTA, ProjectInfo.Quota.fromPb(QUOTA.toPb())); + ProjectInfo.Quota wrong = new ProjectInfo.Quota(5, 6, 3, 6, 2, 1); + assertNotEquals(QUOTA, ProjectInfo.Quota.fromPb(wrong.toPb())); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/RecordSetTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/RecordSetTest.java new file mode 100644 index 000000000000..f5c515b6752d --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/RecordSetTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class RecordSetTest { + + private static final String NAME = "example.com."; + private static final Integer TTL = 3600; + private static final TimeUnit UNIT = TimeUnit.HOURS; + private static final Integer UNIT_TTL = 1; + private static final RecordSet.Type TYPE = RecordSet.Type.AAAA; + private static final RecordSet recordSet = RecordSet.builder(NAME, TYPE) + .ttl(UNIT_TTL, UNIT) + .build(); + + @Test + public void testDefaultDnsRecord() { + RecordSet recordSet = RecordSet.builder(NAME, TYPE).build(); + assertEquals(0, recordSet.records().size()); + assertEquals(TYPE, recordSet.type()); + assertEquals(NAME, recordSet.name()); + } + + @Test + public void testBuilder() { + assertEquals(NAME, recordSet.name()); + assertEquals(TTL, recordSet.ttl()); + assertEquals(TYPE, recordSet.type()); + assertEquals(0, recordSet.records().size()); + // verify that one can add records to the record set + String testingRecord = "Testing recordSet"; + String anotherTestingRecord = "Another recordSet 123"; + RecordSet anotherRecord = recordSet.toBuilder() + .addRecord(testingRecord) + .addRecord(anotherTestingRecord) + .build(); + assertEquals(2, anotherRecord.records().size()); + assertTrue(anotherRecord.records().contains(testingRecord)); + assertTrue(anotherRecord.records().contains(anotherTestingRecord)); + } + + @Test + public void testValidTtl() { + try { + RecordSet.builder(NAME, TYPE).ttl(-1, TimeUnit.SECONDS); + fail("A negative value is not acceptable for ttl."); + } catch (IllegalArgumentException e) { + // expected + } + RecordSet.builder(NAME, TYPE).ttl(0, TimeUnit.SECONDS); + RecordSet.builder(NAME, TYPE).ttl(Integer.MAX_VALUE, TimeUnit.SECONDS); + try { + RecordSet.builder(NAME, TYPE).ttl(Integer.MAX_VALUE, TimeUnit.HOURS); + fail("This value is too large for int."); + } catch (IllegalArgumentException e) { + // expected + } + RecordSet record = RecordSet.builder(NAME, TYPE).ttl(UNIT_TTL, UNIT).build(); + assertEquals(TTL, record.ttl()); + } + + @Test + public void testEqualsAndNotEquals() { + RecordSet clone = recordSet.toBuilder().build(); + assertEquals(recordSet, clone); + clone = recordSet.toBuilder().addRecord("another recordSet").build(); + assertNotEquals(recordSet, clone); + String differentName = "totally different name"; + clone = recordSet.toBuilder().name(differentName).build(); + assertNotEquals(recordSet, clone); + clone = recordSet.toBuilder().ttl(recordSet.ttl() + 1, TimeUnit.SECONDS).build(); + assertNotEquals(recordSet, clone); + clone = recordSet.toBuilder().type(RecordSet.Type.TXT).build(); + assertNotEquals(recordSet, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + int hash = recordSet.hashCode(); + RecordSet clone = recordSet.toBuilder().build(); + assertEquals(clone.hashCode(), hash); + } + + @Test + public void testToAndFromPb() { + assertEquals(recordSet, RecordSet.fromPb(recordSet.toPb())); + RecordSet partial = RecordSet.builder(NAME, TYPE).build(); + assertEquals(partial, RecordSet.fromPb(partial.toPb())); + partial = RecordSet.builder(NAME, TYPE).addRecord("test").build(); + assertEquals(partial, RecordSet.fromPb(partial.toPb())); + partial = RecordSet.builder(NAME, TYPE).ttl(15, TimeUnit.SECONDS).build(); + assertEquals(partial, RecordSet.fromPb(partial.toPb())); + } + + @Test + public void testToBuilder() { + assertEquals(recordSet, recordSet.toBuilder().build()); + RecordSet partial = RecordSet.builder(NAME, TYPE).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = RecordSet.builder(NAME, TYPE).addRecord("test").build(); + assertEquals(partial, partial.toBuilder().build()); + partial = RecordSet.builder(NAME, TYPE).ttl(15, TimeUnit.SECONDS).build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void clearRecordSet() { + // make sure that we are starting not empty + RecordSet clone = + recordSet.toBuilder().addRecord("record").addRecord("another").build(); + assertNotEquals(0, clone.records().size()); + clone = clone.toBuilder().clearRecords().build(); + assertEquals(0, clone.records().size()); + clone.toPb(); // verify that pb allows it + } + + @Test + public void removeFromRecordSet() { + String recordString = "record"; + // make sure that we are starting not empty + RecordSet clone = recordSet.toBuilder().addRecord(recordString).build(); + assertNotEquals(0, clone.records().size()); + clone = clone.toBuilder().removeRecord(recordString).build(); + assertEquals(0, clone.records().size()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/SerializationTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/SerializationTest.java new file mode 100644 index 000000000000..e510850f62ab --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/SerializationTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2016 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.cloud.dns; + +import com.google.cloud.AuthCredentials; +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.Restorable; +import com.google.cloud.RetryParams; +import com.google.common.collect.ImmutableList; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + +public class SerializationTest extends BaseSerializationTest { + + private static final ZoneInfo FULL_ZONE_INFO = Zone.of("some zone name", "www.example.com", + "some descriptions").toBuilder() + .creationTimeMillis(132L) + .generatedId("123333") + .nameServers(ImmutableList.of("server 1", "server 2")) + .nameServerSet("specificationstring") + .build(); + private static final ZoneInfo PARTIAL_ZONE_INFO = Zone.of("some zone name", "www.example.com", + "some descriptions").toBuilder().build(); + private static final ProjectInfo PARTIAL_PROJECT_INFO = ProjectInfo.builder().id("13").build(); + private static final ProjectInfo FULL_PROJECT_INFO = ProjectInfo.builder() + .id("342") + .number(new BigInteger("2343245")) + .quota(new ProjectInfo.Quota(12, 13, 14, 15, 16, 17)) + .build(); + private static final Dns.ZoneListOption ZONE_LIST_OPTION = + Dns.ZoneListOption.dnsName("www.example.com."); + private static final Dns.RecordSetListOption RECORD_SET_LIST_OPTION = + Dns.RecordSetListOption.fields(Dns.RecordSetField.TTL); + private static final Dns.ChangeRequestListOption CHANGE_REQUEST_LIST_OPTION = + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.STATUS); + private static final Dns.ZoneOption ZONE_OPTION = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.ChangeRequestOption CHANGE_REQUEST_OPTION = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS); + private static final Dns.ProjectOption PROJECT_OPTION = + Dns.ProjectOption.fields(Dns.ProjectField.QUOTA); + private static final DnsOptions OPTIONS = DnsOptions.builder() + .projectId("some-unnecessary-project-ID") + .retryParams(RetryParams.defaultInstance()) + .build(); + private static final Dns DNS = OPTIONS.service(); + private static final Zone FULL_ZONE = new Zone(DNS, new ZoneInfo.BuilderImpl(FULL_ZONE_INFO)); + private static final Zone PARTIAL_ZONE = + new Zone(DNS, new ZoneInfo.BuilderImpl(PARTIAL_ZONE_INFO)); + private static final ChangeRequestInfo CHANGE_REQUEST_INFO_PARTIAL = + ChangeRequest.builder().build(); + private static final ChangeRequest CHANGE_REQUEST_PARTIAL = new ChangeRequest(DNS, "name", + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_INFO_PARTIAL)); + private static final RecordSet RECORD_SET_PARTIAL = + RecordSet.builder("www.www.com", RecordSet.Type.AAAA).build(); + private static final RecordSet RECORD_SET_COMPLETE = + RecordSet.builder("www.sadfa.com", RecordSet.Type.A) + .ttl(12, TimeUnit.HOURS) + .addRecord("record") + .build(); + private static final ChangeRequestInfo CHANGE_REQUEST_INFO_COMPLETE = ChangeRequestInfo.builder() + .add(RECORD_SET_COMPLETE) + .delete(RECORD_SET_PARTIAL) + .status(ChangeRequest.Status.PENDING) + .generatedId("some id") + .startTimeMillis(132L) + .build(); + private static final ChangeRequest CHANGE_REQUEST_COMPLETE = new ChangeRequest(DNS, "name", + new ChangeRequestInfo.BuilderImpl(CHANGE_REQUEST_INFO_COMPLETE)); + + @Override + protected Serializable[] serializableObjects() { + DnsOptions options = DnsOptions.builder() + .authCredentials(AuthCredentials.createForAppEngine()) + .projectId("id1") + .build(); + DnsOptions otherOptions = options.toBuilder() + .authCredentials(null) + .build(); + return new Serializable[]{FULL_ZONE_INFO, PARTIAL_ZONE_INFO, ZONE_LIST_OPTION, + RECORD_SET_LIST_OPTION, CHANGE_REQUEST_LIST_OPTION, ZONE_OPTION, CHANGE_REQUEST_OPTION, + PROJECT_OPTION, PARTIAL_PROJECT_INFO, FULL_PROJECT_INFO, OPTIONS, FULL_ZONE, PARTIAL_ZONE, + OPTIONS, CHANGE_REQUEST_INFO_PARTIAL, CHANGE_REQUEST_PARTIAL, RECORD_SET_PARTIAL, + RECORD_SET_COMPLETE, CHANGE_REQUEST_INFO_COMPLETE, CHANGE_REQUEST_COMPLETE, options, + otherOptions}; + } + + @Override + protected Restorable[] restorableObjects() { + return new Restorable[0]; + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneInfoTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneInfoTest.java new file mode 100644 index 000000000000..215d377f6a67 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneInfoTest.java @@ -0,0 +1,166 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.api.services.dns.model.ManagedZone; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; + +public class ZoneInfoTest { + + private static final String NAME = "mz-example.com"; + private static final String GENERATED_ID = "123456"; + private static final Long CREATION_TIME_MILLIS = 1123468321321L; + private static final String DNS_NAME = "example.com."; + private static final String DESCRIPTION = "description for the zone"; + private static final String NAME_SERVER_SET = "some set"; + private static final String NS1 = "name server 1"; + private static final String NS2 = "name server 2"; + private static final String NS3 = "name server 3"; + private static final List NAME_SERVERS = ImmutableList.of(NS1, NS2, NS3); + private static final ZoneInfo INFO = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder() + .creationTimeMillis(CREATION_TIME_MILLIS) + .generatedId(GENERATED_ID) + .nameServerSet(NAME_SERVER_SET) + .nameServers(NAME_SERVERS) + .build(); + + @Test + public void testOf() { + ZoneInfo partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION); + assertTrue(partial.nameServers().isEmpty()); + assertEquals(NAME, partial.name()); + assertNull(partial.generatedId()); + assertNull(partial.creationTimeMillis()); + assertNull(partial.nameServerSet()); + assertEquals(DESCRIPTION, partial.description()); + assertEquals(DNS_NAME, partial.dnsName()); + } + + @Test + public void testBuilder() { + assertEquals(3, INFO.nameServers().size()); + assertEquals(NS1, INFO.nameServers().get(0)); + assertEquals(NS2, INFO.nameServers().get(1)); + assertEquals(NS3, INFO.nameServers().get(2)); + assertEquals(NAME, INFO.name()); + assertEquals(GENERATED_ID, INFO.generatedId()); + assertEquals(CREATION_TIME_MILLIS, INFO.creationTimeMillis()); + assertEquals(NAME_SERVER_SET, INFO.nameServerSet()); + assertEquals(DESCRIPTION, INFO.description()); + assertEquals(DNS_NAME, INFO.dnsName()); + } + + @Test + public void testEqualsAndNotEquals() { + ZoneInfo clone = INFO.toBuilder().build(); + assertEquals(INFO, clone); + List moreServers = Lists.newLinkedList(NAME_SERVERS); + moreServers.add(NS1); + clone = INFO.toBuilder().nameServers(moreServers).build(); + assertNotEquals(INFO, clone); + String differentName = "totally different name"; + clone = INFO.toBuilder().name(differentName).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().creationTimeMillis(INFO.creationTimeMillis() + 1).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().description(INFO.description() + "aaaa").build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().dnsName(differentName).build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().generatedId(INFO.generatedId() + "1111").build(); + assertNotEquals(INFO, clone); + clone = INFO.toBuilder().nameServerSet(INFO.nameServerSet() + "salt").build(); + assertNotEquals(INFO, clone); + } + + @Test + public void testSameHashCodeOnEquals() { + int hash = INFO.hashCode(); + ZoneInfo clone = INFO.toBuilder().build(); + assertEquals(clone.hashCode(), hash); + } + + @Test + public void testToBuilder() { + assertEquals(INFO, INFO.toBuilder().build()); + ZoneInfo partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder() + .generatedId(GENERATED_ID) + .build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder() + .creationTimeMillis(CREATION_TIME_MILLIS).build(); + assertEquals(partial, partial.toBuilder().build()); + List nameServers = new LinkedList<>(); + nameServers.add(NS1); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder().nameServers(nameServers).build(); + assertEquals(partial, partial.toBuilder().build()); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder().nameServerSet(NAME_SERVER_SET) + .build(); + assertEquals(partial, partial.toBuilder().build()); + } + + @Test + public void testToAndFromPb() { + assertEquals(INFO, ZoneInfo.fromPb(INFO.toPb())); + ZoneInfo partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder() + .generatedId(GENERATED_ID) + .build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder() + .creationTimeMillis(CREATION_TIME_MILLIS).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + List nameServers = new LinkedList<>(); + nameServers.add(NS1); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder().nameServers(nameServers).build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + partial = ZoneInfo.of(NAME, DNS_NAME, DESCRIPTION).toBuilder().nameServerSet(NAME_SERVER_SET) + .build(); + assertEquals(partial, ZoneInfo.fromPb(partial.toPb())); + } + + @Test + public void testEmptyNameServers() { + ZoneInfo clone = INFO.toBuilder().nameServers(new LinkedList()).build(); + assertTrue(clone.nameServers().isEmpty()); + clone.toPb(); // test that this is allowed + } + + @Test + public void testDateParsing() { + ManagedZone pb = INFO.toPb(); + pb.setCreationTime("2016-01-19T18:00:12.854Z"); // a real value obtained from Google Cloud DNS + ZoneInfo mz = ZoneInfo.fromPb(pb); // parses the string timestamp to millis + ManagedZone pbClone = mz.toPb(); // converts it back to string + assertEquals(pb, pbClone); + assertEquals(pb.getCreationTime(), pbClone.getCreationTime()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneTest.java new file mode 100644 index 000000000000..e1f6c8917647 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/ZoneTest.java @@ -0,0 +1,502 @@ +/* + * Copyright 2016 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.cloud.dns; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.cloud.Page; +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +public class ZoneTest { + + private static final String ZONE_NAME = "dns-zone-name"; + private static final String ZONE_ID = "123"; + private static final ZoneInfo ZONE_INFO = Zone.of(ZONE_NAME, "example.com", "description") + .toBuilder() + .generatedId(ZONE_ID) + .creationTimeMillis(123478946464L) + .build(); + private static final ZoneInfo NO_ID_INFO = + ZoneInfo.of(ZONE_NAME, "another-example.com", "description").toBuilder() + .creationTimeMillis(893123464L) + .build(); + private static final Dns.ZoneOption ZONE_FIELD_OPTIONS = + Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME); + private static final Dns.RecordSetListOption DNS_RECORD_OPTIONS = + Dns.RecordSetListOption.dnsName("some-dns"); + private static final Dns.ChangeRequestOption CHANGE_REQUEST_FIELD_OPTIONS = + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME); + private static final Dns.ChangeRequestListOption CHANGE_REQUEST_LIST_OPTIONS = + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.START_TIME); + private static final ChangeRequestInfo CHANGE_REQUEST = + ChangeRequestInfo.builder().generatedId("someid").build(); + private static final ChangeRequestInfo CHANGE_REQUEST_NO_ID = + ChangeRequestInfo.builder().build(); + private static final DnsException EXCEPTION = createStrictMock(DnsException.class); + private static final DnsOptions OPTIONS = createStrictMock(DnsOptions.class); + + private Dns dns; + private Zone zone; + private Zone zoneNoId; + private ChangeRequest changeRequestAfter; + + @Before + public void setUp() throws Exception { + dns = createStrictMock(Dns.class); + expect(dns.options()).andReturn(OPTIONS).times(3); + replay(dns); + zone = new Zone(dns, new ZoneInfo.BuilderImpl(ZONE_INFO)); + zoneNoId = new Zone(dns, new ZoneInfo.BuilderImpl(NO_ID_INFO)); + changeRequestAfter = new ChangeRequest(dns, ZONE_NAME, new ChangeRequestInfo.BuilderImpl( + CHANGE_REQUEST.toBuilder().startTimeMillis(123465L).build())); + reset(dns); + } + + @After + public void tearDown() throws Exception { + verify(dns); + } + + @Test + public void testConstructor() { + replay(dns); + assertEquals(ZONE_INFO.toPb(), zone.toPb()); + assertNotNull(zone.dns()); + assertEquals(dns, zone.dns()); + } + + @Test + public void deleteByNameAndFound() { + expect(dns.delete(ZONE_NAME)).andReturn(true).times(2); + replay(dns); + boolean result = zone.delete(); + assertTrue(result); + result = zoneNoId.delete(); + assertTrue(result); + } + + @Test + public void deleteByNameAndNotFound() { + expect(dns.delete(ZONE_NAME)).andReturn(false).times(2); + replay(dns); + boolean result = zoneNoId.delete(); + assertFalse(result); + result = zone.delete(); + assertFalse(result); + } + + @Test + public void listDnsRecordsByNameAndFound() { + @SuppressWarnings("unchecked") + Page pageMock = createStrictMock(Page.class); + replay(pageMock); + expect(dns.listRecordSets(ZONE_NAME)).andReturn(pageMock).times(2); + // again for options + expect(dns.listRecordSets(ZONE_NAME, DNS_RECORD_OPTIONS)).andReturn(pageMock).times(2); + replay(dns); + Page result = zone.listRecordSets(); + assertSame(pageMock, result); + result = zoneNoId.listRecordSets(); + assertSame(pageMock, result); + verify(pageMock); + zone.listRecordSets(DNS_RECORD_OPTIONS); // check options + zoneNoId.listRecordSets(DNS_RECORD_OPTIONS); // check options + } + + @Test + public void listDnsRecordsByNameAndNotFound() { + expect(dns.listRecordSets(ZONE_NAME)).andThrow(EXCEPTION).times(2); + // again for options + expect(dns.listRecordSets(ZONE_NAME, DNS_RECORD_OPTIONS)).andThrow(EXCEPTION).times(2); + replay(dns); + try { + zoneNoId.listRecordSets(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listRecordSets(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zoneNoId.listRecordSets(DNS_RECORD_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listRecordSets(DNS_RECORD_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void reloadByNameAndFound() { + expect(dns.getZone(ZONE_NAME)).andReturn(zone).times(2); + // again for options + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(zoneNoId); + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(zone); + replay(dns); + Zone result = zoneNoId.reload(); + assertSame(zone.dns(), result.dns()); + assertEquals(zone, result); + result = zone.reload(); + assertSame(zone.dns(), result.dns()); + assertEquals(zone, result); + zoneNoId.reload(ZONE_FIELD_OPTIONS); // check options + zone.reload(ZONE_FIELD_OPTIONS); // check options + } + + @Test + public void reloadByNameAndNotFound() { + expect(dns.getZone(ZONE_NAME)).andReturn(null).times(2); + // again for options + expect(dns.getZone(ZONE_NAME, ZONE_FIELD_OPTIONS)).andReturn(null).times(2); + replay(dns); + Zone result = zoneNoId.reload(); + assertNull(result); + result = zone.reload(); + assertNull(result); + zoneNoId.reload(ZONE_FIELD_OPTIONS); // for options + zone.reload(ZONE_FIELD_OPTIONS); // for options + } + + @Test + public void applyChangeByNameAndFound() { + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)) + .andReturn(changeRequestAfter); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)) + .andReturn(changeRequestAfter); + // again for options + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(changeRequestAfter); + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andReturn(changeRequestAfter); + replay(dns); + ChangeRequest result = zoneNoId.applyChangeRequest(CHANGE_REQUEST); + assertEquals(changeRequestAfter, result); + result = zone.applyChangeRequest(CHANGE_REQUEST); + assertEquals(changeRequestAfter, result); + // check options + result = zoneNoId.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(changeRequestAfter, result); + result = zone.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(changeRequestAfter, result); + } + + @Test + public void applyChangeByNameAndNotFound() { + // ID is not set + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST)).andThrow(EXCEPTION).times(2); + // again for options + expect(dns.applyChangeRequest(ZONE_NAME, CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS)) + .andThrow(EXCEPTION).times(2); + replay(dns); + try { + zoneNoId.applyChangeRequest(CHANGE_REQUEST); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.applyChangeRequest(CHANGE_REQUEST); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + // check options + try { + zoneNoId.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.applyChangeRequest(CHANGE_REQUEST, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void applyNullChangeRequest() { + replay(dns); // no calls expected + try { + zone.applyChangeRequest(null); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zone.applyChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.applyChangeRequest(null); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.applyChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot apply null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void getChangeAndZoneFoundByName() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId())) + .andReturn(changeRequestAfter).times(2); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId(), + CHANGE_REQUEST_FIELD_OPTIONS)).andReturn(changeRequestAfter).times(2); + replay(dns); + ChangeRequest result = zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId()); + assertEquals(changeRequestAfter, result); + result = zone.getChangeRequest(CHANGE_REQUEST.generatedId()); + assertEquals(changeRequestAfter, result); + // check options + result = zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(changeRequestAfter, result); + result = zone.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + assertEquals(changeRequestAfter, result); + } + + @Test + public void getChangeAndZoneNotFoundByName() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId())).andThrow(EXCEPTION) + .times(2); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId(), + CHANGE_REQUEST_FIELD_OPTIONS)).andThrow(EXCEPTION).times(2); + replay(dns); + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId()); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST.generatedId()); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + // check options + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void getChangedWhichDoesNotExistZoneFound() { + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId())).andReturn(null).times(2); + // again for options + expect(dns.getChangeRequest(ZONE_NAME, CHANGE_REQUEST.generatedId(), + CHANGE_REQUEST_FIELD_OPTIONS)).andReturn(null).times(2); + replay(dns); + assertNull(zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId())); + assertNull(zone.getChangeRequest(CHANGE_REQUEST.generatedId())); + assertNull( + zoneNoId.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS)); + assertNull(zone.getChangeRequest(CHANGE_REQUEST.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS)); + } + + @Test + public void getNullChangeRequest() { + replay(dns); // no calls expected + try { + zone.getChangeRequest(null); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zone.getChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(null); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(null, CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get null ChangeRequest."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void getChangeRequestWithNoId() { + replay(dns); // no calls expected + try { + zone.getChangeRequest(CHANGE_REQUEST_NO_ID.generatedId()); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zone.getChangeRequest(CHANGE_REQUEST_NO_ID.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST_NO_ID.generatedId()); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + try { + zoneNoId.getChangeRequest(CHANGE_REQUEST_NO_ID.generatedId(), CHANGE_REQUEST_FIELD_OPTIONS); + fail("Cannot get ChangeRequest by null id."); + } catch (NullPointerException e) { + // expected + } + } + + @Test + public void listChangeRequestsAndZoneFound() { + @SuppressWarnings("unchecked") + Page pageMock = createStrictMock(Page.class); + replay(pageMock); + expect(dns.listChangeRequests(ZONE_NAME)).andReturn(pageMock).times(2); + // again for options + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)) + .andReturn(pageMock).times(2); + replay(dns); + Page result = zoneNoId.listChangeRequests(); + assertSame(pageMock, result); + result = zone.listChangeRequests(); + assertSame(pageMock, result); + verify(pageMock); + zoneNoId.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + zone.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + } + + @Test + public void listChangeRequestsAndZoneNotFound() { + expect(dns.listChangeRequests(ZONE_NAME)).andThrow(EXCEPTION).times(2); + // again for options + expect(dns.listChangeRequests(ZONE_NAME, CHANGE_REQUEST_LIST_OPTIONS)).andThrow(EXCEPTION) + .times(2); + replay(dns); + try { + zoneNoId.listChangeRequests(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listChangeRequests(); + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zoneNoId.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + try { + zone.listChangeRequests(CHANGE_REQUEST_LIST_OPTIONS); // check options + fail("Parent container not found, should throw an exception."); + } catch (DnsException e) { + // expected + } + } + + @Test + public void testFromPb() { + expect(dns.options()).andReturn(OPTIONS); + replay(dns); + assertEquals(Zone.fromPb(dns, zone.toPb()), zone); + } + + @Test + public void testEqualsAndToBuilder() { + expect(dns.options()).andReturn(OPTIONS).times(2); + replay(dns); + assertEquals(zone, zone.toBuilder().build()); + assertEquals(zone.hashCode(), zone.toBuilder().build().hashCode()); + } + + @Test + public void testBuilder() { + // one for each build() call because it invokes a constructor + expect(dns.options()).andReturn(OPTIONS).times(8); + replay(dns); + assertNotEquals(zone, zone.toBuilder() + .generatedId(new BigInteger(zone.generatedId()).add(BigInteger.ONE).toString()) + .build()); + assertNotEquals(zone, zone.toBuilder().dnsName(zone.name() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().nameServerSet(zone.nameServerSet() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().nameServers(ImmutableList.of("nameserverpppp")).build()); + assertNotEquals(zone, zone.toBuilder().dnsName(zone.dnsName() + "aaaa").build()); + assertNotEquals(zone, zone.toBuilder().creationTimeMillis(zone.creationTimeMillis() + 1) + .build()); + Zone.Builder builder = zone.toBuilder(); + builder.generatedId(ZONE_ID) + .dnsName("example.com") + .creationTimeMillis(123478946464L) + .build(); + assertEquals(zone, builder.build()); + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/it/ITDnsTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/it/ITDnsTest.java new file mode 100644 index 000000000000..dd8eafa775cd --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/it/ITDnsTest.java @@ -0,0 +1,965 @@ +/* + * Copyright 2016 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.cloud.dns.it; + +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.cloud.Page; +import com.google.cloud.dns.ChangeRequest; +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsException; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.ProjectInfo; +import com.google.cloud.dns.RecordSet; +import com.google.cloud.dns.Zone; +import com.google.cloud.dns.ZoneInfo; +import com.google.common.collect.ImmutableList; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class ITDnsTest { + + private static final String PREFIX = "gcldjvit-"; + private static final Dns DNS = DnsOptions.defaultInstance().service(); + private static final String ZONE_NAME1 = (PREFIX + UUID.randomUUID()).substring(0, 32); + private static final String ZONE_NAME_EMPTY_DESCRIPTION = + (PREFIX + UUID.randomUUID()).substring(0, 32); + private static final String ZONE_NAME_TOO_LONG = PREFIX + UUID.randomUUID(); + private static final String ZONE_DESCRIPTION1 = "first zone"; + private static final String ZONE_DNS_NAME1 = ZONE_NAME1 + ".com."; + private static final String ZONE_DNS_EMPTY_DESCRIPTION = ZONE_NAME_EMPTY_DESCRIPTION + ".com."; + private static final String ZONE_DNS_NAME_NO_PERIOD = ZONE_NAME1 + ".com"; + private static final ZoneInfo ZONE1 = + ZoneInfo.of(ZONE_NAME1, ZONE_DNS_EMPTY_DESCRIPTION, ZONE_DESCRIPTION1); + private static final ZoneInfo ZONE_EMPTY_DESCRIPTION = + ZoneInfo.of(ZONE_NAME_EMPTY_DESCRIPTION, ZONE_DNS_NAME1, ZONE_DESCRIPTION1); + private static final ZoneInfo ZONE_NAME_ERROR = + ZoneInfo.of(ZONE_NAME_TOO_LONG, ZONE_DNS_NAME1, ZONE_DESCRIPTION1); + private static final ZoneInfo ZONE_DNS_NO_PERIOD = + ZoneInfo.of(ZONE_NAME1, ZONE_DNS_NAME_NO_PERIOD, ZONE_DESCRIPTION1); + private static final RecordSet A_RECORD_ZONE1 = + RecordSet.builder("www." + ZONE1.dnsName(), RecordSet.Type.A) + .records(ImmutableList.of("123.123.55.1")) + .ttl(25, TimeUnit.SECONDS) + .build(); + private static final RecordSet AAAA_RECORD_ZONE1 = + RecordSet.builder("www." + ZONE1.dnsName(), RecordSet.Type.AAAA) + .records(ImmutableList.of("ed:ed:12:aa:36:3:3:105")) + .ttl(25, TimeUnit.SECONDS) + .build(); + private static final ChangeRequestInfo CHANGE_ADD_ZONE1 = ChangeRequest.builder() + .add(A_RECORD_ZONE1) + .add(AAAA_RECORD_ZONE1) + .build(); + private static final ChangeRequestInfo CHANGE_DELETE_ZONE1 = ChangeRequest.builder() + .delete(A_RECORD_ZONE1) + .delete(AAAA_RECORD_ZONE1) + .build(); + private static final List ZONE_NAMES = ImmutableList.of(ZONE_NAME1, + ZONE_NAME_EMPTY_DESCRIPTION); + + @Rule + public Timeout globalTimeout = Timeout.seconds(300); + + private static void clear() { + for (String zoneName : ZONE_NAMES) { + Zone zone = DNS.getZone(zoneName); + if (zone != null) { + /* We wait for all changes to complete before retrieving a list of DNS records to be + deleted. Waiting is necessary as changes potentially might create more records between + when the list has been retrieved and executing the subsequent delete operation. */ + Iterator iterator = zone.listChangeRequests().iterateAll(); + while (iterator.hasNext()) { + waitForChangeToComplete(zoneName, iterator.next().generatedId()); + } + Iterator recordSetIterator = zone.listRecordSets().iterateAll(); + List toDelete = new LinkedList<>(); + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + if (!ImmutableList.of(RecordSet.Type.NS, RecordSet.Type.SOA) + .contains(recordSet.type())) { + toDelete.add(recordSet); + } + } + if (!toDelete.isEmpty()) { + ChangeRequest deletion = + zone.applyChangeRequest(ChangeRequest.builder().deletions(toDelete).build()); + waitForChangeToComplete(zone.name(), deletion.generatedId()); + } + zone.delete(); + } + } + } + + private static List filter(Iterator iterator) { + List result = new LinkedList<>(); + while (iterator.hasNext()) { + Zone zone = iterator.next(); + if (ZONE_NAMES.contains(zone.name())) { + result.add(zone); + } + } + return result; + } + + @BeforeClass + public static void before() { + clear(); + } + + @AfterClass + public static void after() { + clear(); + } + + private static void assertEqChangesIgnoreStatus(ChangeRequest expected, ChangeRequest actual) { + assertEquals(expected.additions(), actual.additions()); + assertEquals(expected.deletions(), actual.deletions()); + assertEquals(expected.generatedId(), actual.generatedId()); + assertEquals(expected.startTimeMillis(), actual.startTimeMillis()); + } + + private static void waitForChangeToComplete(String zoneName, String changeId) { + ChangeRequest changeRequest = DNS.getChangeRequest(zoneName, changeId, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS)); + waitForChangeToComplete(changeRequest); + } + + private static void waitForChangeToComplete(ChangeRequest changeRequest) { + while (!changeRequest.isDone()) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + fail("Thread was interrupted while waiting for change processing."); + } + } + } + + @Test + public void testCreateValidZone() { + try { + Zone created = DNS.create(ZONE1); + assertEquals(ZONE1.description(), created.description()); + assertEquals(ZONE1.dnsName(), created.dnsName()); + assertEquals(ZONE1.name(), created.name()); + assertNotNull(created.creationTimeMillis()); + assertNotNull(created.nameServers()); + assertNull(created.nameServerSet()); + assertNotNull(created.generatedId()); + Zone retrieved = DNS.getZone(ZONE1.name()); + assertEquals(created, retrieved); + created = DNS.create(ZONE_EMPTY_DESCRIPTION); + assertEquals(ZONE_EMPTY_DESCRIPTION.description(), created.description()); + assertEquals(ZONE_EMPTY_DESCRIPTION.dnsName(), created.dnsName()); + assertEquals(ZONE_EMPTY_DESCRIPTION.name(), created.name()); + assertNotNull(created.creationTimeMillis()); + assertNotNull(created.nameServers()); + assertNull(created.nameServerSet()); + assertNotNull(created.generatedId()); + retrieved = DNS.getZone(ZONE_EMPTY_DESCRIPTION.name()); + assertEquals(created, retrieved); + } finally { + DNS.delete(ZONE1.name()); + DNS.delete(ZONE_EMPTY_DESCRIPTION.name()); + } + } + + @Test + public void testCreateZoneWithErrors() { + try { + try { + DNS.create(ZONE_NAME_ERROR); + fail("Zone name is missing a period. The service returns an error."); + } catch (DnsException ex) { + // expected + assertFalse(ex.retryable()); + } + try { + DNS.create(ZONE_DNS_NO_PERIOD); + fail("Zone name is missing a period. The service returns an error."); + } catch (DnsException ex) { + // expected + assertFalse(ex.retryable()); + } + } finally { + DNS.delete(ZONE_NAME_ERROR.name()); + DNS.delete(ZONE_DNS_NO_PERIOD.name()); + } + } + + @Test + public void testCreateZoneWithOptions() { + try { + Zone created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNotNull(created.creationTimeMillis()); + assertNull(created.description()); + assertNull(created.dnsName()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.DESCRIPTION)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertEquals(ZONE1.description(), created.description()); + assertNull(created.dnsName()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.DNS_NAME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertEquals(ZONE1.dnsName(), created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME_SERVER_SET)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); // we did not set it + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME_SERVERS)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertFalse(created.nameServers().isEmpty()); + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created.delete(); + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.ZONE_ID)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertNotNull(created.nameServers()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNotNull(created.generatedId()); + created.delete(); + // combination of multiple things + created = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.ZONE_ID, + Dns.ZoneField.NAME_SERVERS, Dns.ZoneField.NAME_SERVER_SET, Dns.ZoneField.DESCRIPTION)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertEquals(ZONE1.description(), created.description()); + assertFalse(created.nameServers().isEmpty()); + assertNull(created.nameServerSet()); // we did not set it + assertNotNull(created.generatedId()); + } finally { + DNS.delete(ZONE1.name()); + } + } + + @Test + public void testGetZone() { + try { + DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME)); + Zone created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.CREATION_TIME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNotNull(created.creationTimeMillis()); + assertNull(created.description()); + assertNull(created.dnsName()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.DESCRIPTION)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertEquals(ZONE1.description(), created.description()); + assertNull(created.dnsName()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.DNS_NAME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertEquals(ZONE1.dnsName(), created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.NAME)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.NAME_SERVER_SET)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNull(created.nameServerSet()); // we did not set it + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.NAME_SERVERS)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertFalse(created.nameServers().isEmpty()); + assertNull(created.nameServerSet()); + assertNull(created.generatedId()); + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.ZONE_ID)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertNull(created.description()); + assertNotNull(created.nameServers()); + assertTrue(created.nameServers().isEmpty()); // never returns null + assertNotNull(created.generatedId()); + // combination of multiple things + created = DNS.getZone(ZONE1.name(), Dns.ZoneOption.fields(Dns.ZoneField.ZONE_ID, + Dns.ZoneField.NAME_SERVERS, Dns.ZoneField.NAME_SERVER_SET, Dns.ZoneField.DESCRIPTION)); + assertEquals(ZONE1.name(), created.name()); // always returned + assertNull(created.creationTimeMillis()); + assertNull(created.dnsName()); + assertEquals(ZONE1.description(), created.description()); + assertFalse(created.nameServers().isEmpty()); + assertNull(created.nameServerSet()); // we did not set it + assertNotNull(created.generatedId()); + } finally { + DNS.delete(ZONE1.name()); + } + } + + @Test + public void testListZones() { + try { + List zones = filter(DNS.listZones().iterateAll()); + assertEquals(0, zones.size()); + // some zones exists + Zone created = DNS.create(ZONE1); + zones = filter(DNS.listZones().iterateAll()); + assertEquals(created, zones.get(0)); + assertEquals(1, zones.size()); + created = DNS.create(ZONE_EMPTY_DESCRIPTION); + zones = filter(DNS.listZones().iterateAll()); + assertEquals(2, zones.size()); + assertTrue(zones.contains(created)); + // error in options + try { + DNS.listZones(Dns.ZoneListOption.pageSize(0)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + try { + DNS.listZones(Dns.ZoneListOption.pageSize(-1)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + // ok size + zones = filter(DNS.listZones(Dns.ZoneListOption.pageSize(1000)).iterateAll()); + assertEquals(2, zones.size()); // we still have only 2 zones + // dns name problems + try { + DNS.listZones(Dns.ZoneListOption.dnsName("aaaaa")); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + // ok name + zones = filter(DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName())).iterateAll()); + assertEquals(1, zones.size()); + // field options + Iterator zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.ZONE_ID)).iterateAll(); + Zone zone = zoneIterator.next(); + assertNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNull(zone.dnsName()); + assertNull(zone.description()); + assertNull(zone.nameServerSet()); + assertTrue(zone.nameServers().isEmpty()); + assertNotNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.CREATION_TIME)).iterateAll(); + zone = zoneIterator.next(); + assertNotNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNull(zone.dnsName()); + assertNull(zone.description()); + assertNull(zone.nameServerSet()); + assertTrue(zone.nameServers().isEmpty()); + assertNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.DNS_NAME)).iterateAll(); + zone = zoneIterator.next(); + assertNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNotNull(zone.dnsName()); + assertNull(zone.description()); + assertNull(zone.nameServerSet()); + assertTrue(zone.nameServers().isEmpty()); + assertNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.DESCRIPTION)).iterateAll(); + zone = zoneIterator.next(); + assertNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNull(zone.dnsName()); + assertNotNull(zone.description()); + assertNull(zone.nameServerSet()); + assertTrue(zone.nameServers().isEmpty()); + assertNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.NAME_SERVERS)).iterateAll(); + zone = zoneIterator.next(); + assertNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNull(zone.dnsName()); + assertNull(zone.description()); + assertNull(zone.nameServerSet()); + assertFalse(zone.nameServers().isEmpty()); + assertNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + zoneIterator = DNS.listZones(Dns.ZoneListOption.dnsName(ZONE1.dnsName()), + Dns.ZoneListOption.fields(Dns.ZoneField.NAME_SERVER_SET)).iterateAll(); + zone = zoneIterator.next(); + assertNull(zone.creationTimeMillis()); + assertNotNull(zone.name()); + assertNull(zone.dnsName()); + assertNull(zone.description()); + assertNull(zone.nameServerSet()); // we cannot set it using gcloud java + assertTrue(zone.nameServers().isEmpty()); + assertNull(zone.generatedId()); + assertFalse(zoneIterator.hasNext()); + // several combined + zones = filter(DNS.listZones(Dns.ZoneListOption.fields(Dns.ZoneField.ZONE_ID, + Dns.ZoneField.DESCRIPTION), + Dns.ZoneListOption.pageSize(1)).iterateAll()); + assertEquals(2, zones.size()); + for (Zone current : zones) { + assertNull(current.creationTimeMillis()); + assertNotNull(current.name()); + assertNull(current.dnsName()); + assertNotNull(current.description()); + assertNull(current.nameServerSet()); + assertTrue(zone.nameServers().isEmpty()); + assertNotNull(current.generatedId()); + } + } finally { + DNS.delete(ZONE1.name()); + DNS.delete(ZONE_EMPTY_DESCRIPTION.name()); + } + } + + @Test + public void testDeleteZone() { + try { + Zone created = DNS.create(ZONE1); + assertEquals(created, DNS.getZone(ZONE1.name())); + DNS.delete(ZONE1.name()); + assertNull(DNS.getZone(ZONE1.name())); + } finally { + DNS.delete(ZONE1.name()); + } + } + + @Test + public void testCreateChange() { + try { + DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME)); + ChangeRequest created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1); + assertEquals(CHANGE_ADD_ZONE1.additions(), created.additions()); + assertNotNull(created.startTimeMillis()); + assertTrue(created.deletions().isEmpty()); + assertEquals("1", created.generatedId()); + assertTrue(ImmutableList.of(ChangeRequest.Status.PENDING, ChangeRequest.Status.DONE) + .contains(created.status())); + assertEqChangesIgnoreStatus(created, DNS.getChangeRequest(ZONE1.name(), "1")); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(created); + // with options + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ID)); + assertTrue(created.additions().isEmpty()); + assertNull(created.startTimeMillis()); + assertTrue(created.deletions().isEmpty()); + assertEquals("3", created.generatedId()); + assertNull(created.status()); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS)); + assertTrue(created.additions().isEmpty()); + assertNull(created.startTimeMillis()); + assertTrue(created.deletions().isEmpty()); + assertEquals("5", created.generatedId()); + assertNotNull(created.status()); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME)); + assertTrue(created.additions().isEmpty()); + assertNotNull(created.startTimeMillis()); + assertTrue(created.deletions().isEmpty()); + assertEquals("7", created.generatedId()); + assertNull(created.status()); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ADDITIONS)); + assertEquals(CHANGE_ADD_ZONE1.additions(), created.additions()); + assertNull(created.startTimeMillis()); + assertTrue(created.deletions().isEmpty()); + assertEquals("9", created.generatedId()); + assertNull(created.status()); + // finishes with delete otherwise we cannot delete the zone + waitForChangeToComplete(created); + created = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.DELETIONS)); + waitForChangeToComplete(created); + assertEquals(CHANGE_DELETE_ZONE1.deletions(), created.deletions()); + assertNull(created.startTimeMillis()); + assertTrue(created.additions().isEmpty()); + assertEquals("10", created.generatedId()); + assertNull(created.status()); + waitForChangeToComplete(created); + } finally { + clear(); + } + } + + @Test + public void testInvalidChangeRequest() { + Zone zone = DNS.create(ZONE1); + RecordSet validA = + RecordSet.builder("subdomain." + zone.dnsName(), RecordSet.Type.A) + .records(ImmutableList.of("0.255.1.5")) + .build(); + boolean recordAdded = false; + try { + ChangeRequestInfo validChange = ChangeRequest.builder().add(validA).build(); + zone.applyChangeRequest(validChange); + recordAdded = true; + try { + zone.applyChangeRequest(validChange); + fail("Created a record set which already exists."); + } catch (DnsException ex) { + // expected + assertFalse(ex.retryable()); + assertEquals(409, ex.code()); + } + // delete with field mismatch + RecordSet mismatch = validA.toBuilder().ttl(20, TimeUnit.SECONDS).build(); + ChangeRequestInfo deletion = ChangeRequest.builder().delete(mismatch).build(); + try { + zone.applyChangeRequest(deletion); + fail("Deleted a record set without a complete match."); + } catch (DnsException ex) { + // expected + assertEquals(412, ex.code()); + assertFalse(ex.retryable()); + } + // delete and add SOA + Iterator recordSetIterator = zone.listRecordSets().iterateAll(); + LinkedList deletions = new LinkedList<>(); + LinkedList additions = new LinkedList<>(); + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + if (recordSet.type() == RecordSet.Type.SOA) { + deletions.add(recordSet); + // the subdomain is necessary to get 400 instead of 412 + RecordSet copy = recordSet.toBuilder().name("x." + recordSet.name()).build(); + additions.add(copy); + break; + } + } + deletion = deletion.toBuilder().deletions(deletions).build(); + ChangeRequestInfo addition = ChangeRequest.builder().additions(additions).build(); + try { + zone.applyChangeRequest(deletion); + fail("Deleted SOA."); + } catch (DnsException ex) { + // expected + assertFalse(ex.retryable()); + assertEquals(400, ex.code()); + } + try { + zone.applyChangeRequest(addition); + fail("Added second SOA."); + } catch (DnsException ex) { + // expected + assertFalse(ex.retryable()); + assertEquals(400, ex.code()); + } + } finally { + if (recordAdded) { + ChangeRequestInfo deletion = ChangeRequest.builder().delete(validA).build(); + ChangeRequest request = zone.applyChangeRequest(deletion); + waitForChangeToComplete(zone.name(), request.generatedId()); + } + zone.delete(); + } + } + + @Test + public void testListChanges() { + try { + // no such zone exists + try { + DNS.listChangeRequests(ZONE1.name()); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(404, ex.code()); + assertFalse(ex.retryable()); + } + // zone exists but has no changes + DNS.create(ZONE1); + ImmutableList changes = ImmutableList.copyOf( + DNS.listChangeRequests(ZONE1.name()).iterateAll()); + assertEquals(1, changes.size()); // default change creating SOA and NS + // zone has changes + ChangeRequest change = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1); + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + change = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + change = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1); + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + change = DNS.applyChangeRequest(ZONE1.name(), CHANGE_DELETE_ZONE1); + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name()).iterateAll()); + assertEquals(5, changes.size()); + // error in options + try { + DNS.listChangeRequests(ZONE1.name(), Dns.ChangeRequestListOption.pageSize(0)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + try { + DNS.listChangeRequests(ZONE1.name(), Dns.ChangeRequestListOption.pageSize(-1)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + // sorting order + ImmutableList ascending = ImmutableList.copyOf(DNS.listChangeRequests( + ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING)).iterateAll()); + ImmutableList descending = ImmutableList.copyOf(DNS.listChangeRequests( + ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.DESCENDING)).iterateAll()); + int size = 5; + assertEquals(size, descending.size()); + assertEquals(size, ascending.size()); + for (int i = 0; i < size; i++) { + assertEquals(descending.get(i), ascending.get(size - i - 1)); + } + // field options + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.ADDITIONS)).iterateAll()); + change = changes.get(1); + assertEquals(CHANGE_ADD_ZONE1.additions(), change.additions()); + assertTrue(change.deletions().isEmpty()); + assertEquals("1", change.generatedId()); + assertNull(change.startTimeMillis()); + assertNull(change.status()); + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.DELETIONS)).iterateAll()); + change = changes.get(2); + assertTrue(change.additions().isEmpty()); + assertNotNull(change.deletions()); + assertEquals("2", change.generatedId()); + assertNull(change.startTimeMillis()); + assertNull(change.status()); + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.ID)).iterateAll()); + change = changes.get(1); + assertTrue(change.additions().isEmpty()); + assertTrue(change.deletions().isEmpty()); + assertEquals("1", change.generatedId()); + assertNull(change.startTimeMillis()); + assertNull(change.status()); + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.START_TIME)).iterateAll()); + change = changes.get(1); + assertTrue(change.additions().isEmpty()); + assertTrue(change.deletions().isEmpty()); + assertEquals("1", change.generatedId()); + assertNotNull(change.startTimeMillis()); + assertNull(change.status()); + changes = ImmutableList.copyOf(DNS.listChangeRequests(ZONE1.name(), + Dns.ChangeRequestListOption.sortOrder(Dns.SortingOrder.ASCENDING), + Dns.ChangeRequestListOption.fields(Dns.ChangeRequestField.STATUS)).iterateAll()); + change = changes.get(1); + assertTrue(change.additions().isEmpty()); + assertTrue(change.deletions().isEmpty()); + assertEquals("1", change.generatedId()); + assertNull(change.startTimeMillis()); + assertEquals(ChangeRequest.Status.DONE, change.status()); + } finally { + clear(); + } + } + + @Test + public void testGetChange() { + try { + Zone zone = DNS.create(ZONE1, Dns.ZoneOption.fields(Dns.ZoneField.NAME)); + ChangeRequest created = zone.applyChangeRequest(CHANGE_ADD_ZONE1); + ChangeRequest retrieved = DNS.getChangeRequest(zone.name(), created.generatedId()); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + zone.applyChangeRequest(CHANGE_DELETE_ZONE1); + // with options + created = zone.applyChangeRequest(CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ID)); + retrieved = DNS.getChangeRequest(zone.name(), created.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ID)); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + zone.applyChangeRequest(CHANGE_DELETE_ZONE1); + created = zone.applyChangeRequest(CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS)); + retrieved = DNS.getChangeRequest(zone.name(), created.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.STATUS)); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + zone.applyChangeRequest(CHANGE_DELETE_ZONE1); + created = zone.applyChangeRequest(CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME)); + retrieved = DNS.getChangeRequest(zone.name(), created.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.START_TIME)); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + zone.applyChangeRequest(CHANGE_DELETE_ZONE1); + created = zone.applyChangeRequest(CHANGE_ADD_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ADDITIONS)); + retrieved = DNS.getChangeRequest(zone.name(), created.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.ADDITIONS)); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + // finishes with delete otherwise we cannot delete the zone + created = zone.applyChangeRequest(CHANGE_DELETE_ZONE1, + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.DELETIONS)); + retrieved = DNS.getChangeRequest(zone.name(), created.generatedId(), + Dns.ChangeRequestOption.fields(Dns.ChangeRequestField.DELETIONS)); + assertEqChangesIgnoreStatus(created, retrieved); + waitForChangeToComplete(zone.name(), created.generatedId()); + } finally { + clear(); + } + } + + @Test + public void testGetProject() { + // fetches all fields + ProjectInfo project = DNS.getProject(); + assertNotNull(project.quota()); + // options + project = DNS.getProject(Dns.ProjectOption.fields(Dns.ProjectField.QUOTA)); + assertNotNull(project.quota()); + project = DNS.getProject(Dns.ProjectOption.fields(Dns.ProjectField.PROJECT_ID)); + assertNull(project.quota()); + project = DNS.getProject(Dns.ProjectOption.fields(Dns.ProjectField.PROJECT_NUMBER)); + assertNull(project.quota()); + project = DNS.getProject(Dns.ProjectOption.fields(Dns.ProjectField.PROJECT_NUMBER, + Dns.ProjectField.QUOTA, Dns.ProjectField.PROJECT_ID)); + assertNotNull(project.quota()); + } + + @Test + public void testListDnsRecords() { + try { + Zone zone = DNS.create(ZONE1); + ImmutableList recordSets = ImmutableList.copyOf( + DNS.listRecordSets(zone.name()).iterateAll()); + assertEquals(2, recordSets.size()); + ImmutableList defaultRecords = + ImmutableList.of(RecordSet.Type.NS, RecordSet.Type.SOA); + for (RecordSet recordSet : recordSets) { + assertTrue(defaultRecords.contains(recordSet.type())); + } + // field options + Iterator recordSetIterator = DNS.listRecordSets(zone.name(), + Dns.RecordSetListOption.fields(Dns.RecordSetField.TTL)).iterateAll(); + int counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertEquals(recordSets.get(counter).ttl(), recordSet.ttl()); + assertEquals(recordSets.get(counter).name(), recordSet.name()); + assertEquals(recordSets.get(counter).type(), recordSet.type()); + assertTrue(recordSet.records().isEmpty()); + counter++; + } + assertEquals(2, counter); + recordSetIterator = DNS.listRecordSets(zone.name(), + Dns.RecordSetListOption.fields(Dns.RecordSetField.NAME)).iterateAll(); + counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertEquals(recordSets.get(counter).name(), recordSet.name()); + assertEquals(recordSets.get(counter).type(), recordSet.type()); + assertTrue(recordSet.records().isEmpty()); + assertNull(recordSet.ttl()); + counter++; + } + assertEquals(2, counter); + recordSetIterator = DNS.listRecordSets(zone.name(), + Dns.RecordSetListOption.fields(Dns.RecordSetField.DNS_RECORDS)) + .iterateAll(); + counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertEquals(recordSets.get(counter).records(), recordSet.records()); + assertEquals(recordSets.get(counter).name(), recordSet.name()); + assertEquals(recordSets.get(counter).type(), recordSet.type()); + assertNull(recordSet.ttl()); + counter++; + } + assertEquals(2, counter); + recordSetIterator = DNS.listRecordSets(zone.name(), + Dns.RecordSetListOption.fields(Dns.RecordSetField.TYPE), + Dns.RecordSetListOption.pageSize(1)).iterateAll(); // also test paging + counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertEquals(recordSets.get(counter).type(), recordSet.type()); + assertEquals(recordSets.get(counter).name(), recordSet.name()); + assertTrue(recordSet.records().isEmpty()); + assertNull(recordSet.ttl()); + counter++; + } + assertEquals(2, counter); + // test page size + Page recordSetPage = DNS.listRecordSets(zone.name(), + Dns.RecordSetListOption.fields(Dns.RecordSetField.TYPE), + Dns.RecordSetListOption.pageSize(1)); + assertEquals(1, ImmutableList.copyOf(recordSetPage.values().iterator()).size()); + // test name filter + ChangeRequest change = DNS.applyChangeRequest(ZONE1.name(), CHANGE_ADD_ZONE1); + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + recordSetIterator = DNS.listRecordSets(ZONE1.name(), + Dns.RecordSetListOption.dnsName(A_RECORD_ZONE1.name())).iterateAll(); + counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertTrue(ImmutableList.of(A_RECORD_ZONE1.type(), AAAA_RECORD_ZONE1.type()) + .contains(recordSet.type())); + counter++; + } + assertEquals(2, counter); + // test type filter + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + recordSetIterator = DNS.listRecordSets(ZONE1.name(), + Dns.RecordSetListOption.dnsName(A_RECORD_ZONE1.name()), + Dns.RecordSetListOption.type(A_RECORD_ZONE1.type())) + .iterateAll(); + counter = 0; + while (recordSetIterator.hasNext()) { + RecordSet recordSet = recordSetIterator.next(); + assertEquals(A_RECORD_ZONE1, recordSet); + counter++; + } + assertEquals(1, counter); + change = zone.applyChangeRequest(CHANGE_DELETE_ZONE1); + // check wrong arguments + try { + // name is not set + DNS.listRecordSets(ZONE1.name(), + Dns.RecordSetListOption.type(A_RECORD_ZONE1.type())); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + try { + DNS.listRecordSets(ZONE1.name(), Dns.RecordSetListOption.pageSize(0)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + try { + DNS.listRecordSets(ZONE1.name(), Dns.RecordSetListOption.pageSize(-1)); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertFalse(ex.retryable()); + } + waitForChangeToComplete(ZONE1.name(), change.generatedId()); + } finally { + clear(); + } + } +} diff --git a/gcloud-java-dns/src/test/java/com/google/cloud/dns/testing/LocalDnsHelperTest.java b/gcloud-java-dns/src/test/java/com/google/cloud/dns/testing/LocalDnsHelperTest.java new file mode 100644 index 000000000000..fce958d3a126 --- /dev/null +++ b/gcloud-java-dns/src/test/java/com/google/cloud/dns/testing/LocalDnsHelperTest.java @@ -0,0 +1,1448 @@ +/* + * Copyright 2016 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.cloud.dns.testing; + +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.api.services.dns.model.Change; +import com.google.api.services.dns.model.ManagedZone; +import com.google.api.services.dns.model.Project; +import com.google.api.services.dns.model.ResourceRecordSet; +import com.google.cloud.dns.DnsException; +import com.google.cloud.dns.spi.DefaultDnsRpc; +import com.google.cloud.dns.spi.DnsRpc; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class LocalDnsHelperTest { + + private static final String RRSET_TYPE = "A"; + private static final ResourceRecordSet RRSET1 = new ResourceRecordSet(); + private static final ResourceRecordSet RRSET2 = new ResourceRecordSet(); + private static final ResourceRecordSet RRSET_KEEP = new ResourceRecordSet(); + private static final String PROJECT_ID1 = "2135436541254"; + private static final String PROJECT_ID2 = "882248761325"; + private static final String ZONE_NAME1 = "my-little-zone"; + private static final String ZONE_NAME2 = "another-zone-name"; + private static final ManagedZone ZONE1 = new ManagedZone(); + private static final ManagedZone ZONE2 = new ManagedZone(); + private static final String DNS_NAME = "www.example.com."; + private static final Change CHANGE1 = new Change(); + private static final Change CHANGE2 = new Change(); + private static final Change CHANGE_KEEP = new Change(); + private static final Change CHANGE_COMPLEX = new Change(); + private static final LocalDnsHelper LOCAL_DNS_HELPER = LocalDnsHelper.create(0L); + private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); + private static final DnsRpc RPC = new DefaultDnsRpc(LOCAL_DNS_HELPER.options()); + private static final String REAL_PROJECT_ID = LOCAL_DNS_HELPER.options().projectId(); + private Map optionsMap; + + @BeforeClass + public static void before() { + ZONE1.setName(ZONE_NAME1); + ZONE1.setDescription(""); + ZONE1.setDnsName(DNS_NAME); + ZONE1.setNameServerSet("somenameserverset"); + ZONE2.setName(ZONE_NAME2); + ZONE2.setDescription(""); + ZONE2.setDnsName(DNS_NAME); + ZONE2.setNameServerSet("somenameserverset"); + RRSET1.setName(DNS_NAME); + RRSET1.setType(RRSET_TYPE); + RRSET1.setRrdatas(ImmutableList.of("1.1.1.1")); + RRSET2.setName(DNS_NAME); + RRSET2.setType(RRSET_TYPE); + RRSET2.setRrdatas(ImmutableList.of("123.132.153.156")); + RRSET_KEEP.setName(DNS_NAME); + RRSET_KEEP.setType("MX"); + RRSET_KEEP.setRrdatas(ImmutableList.of("255.255.255.254")); + CHANGE1.setAdditions(ImmutableList.of(RRSET1, RRSET2)); + CHANGE2.setDeletions(ImmutableList.of(RRSET2)); + CHANGE_KEEP.setAdditions(ImmutableList.of(RRSET_KEEP)); + CHANGE_COMPLEX.setAdditions(ImmutableList.of(RRSET_KEEP)); + CHANGE_COMPLEX.setDeletions(ImmutableList.of(RRSET_KEEP)); + LOCAL_DNS_HELPER.start(); + } + + @Rule + public Timeout globalTimeout = Timeout.seconds(60); + + @Before + public void setUp() { + resetProjects(); + optionsMap = new HashMap<>(); + } + + @AfterClass + public static void after() { + LOCAL_DNS_HELPER.stop(); + } + + private static void resetProjects() { + for (String project : LOCAL_DNS_HELPER.projects().keySet()) { + LOCAL_DNS_HELPER.projects().remove(project); + } + } + + @Test + public void testCreateZone() { + ManagedZone created = RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + // check that default records were created + DnsRpc.ListResult listResult + = RPC.listRecordSets(ZONE1.getName(), EMPTY_RPC_OPTIONS); + ImmutableList defaultTypes = ImmutableList.of("SOA", "NS"); + Iterator iterator = listResult.results().iterator(); + assertTrue(defaultTypes.contains(iterator.next().getType())); + assertTrue(defaultTypes.contains(iterator.next().getType())); + assertFalse(iterator.hasNext()); + assertEquals(created, LOCAL_DNS_HELPER.findZone(REAL_PROJECT_ID, ZONE1.getName()).zone()); + ManagedZone zone = RPC.getZone(ZONE_NAME1, EMPTY_RPC_OPTIONS); + assertEquals(created, zone); + try { + RPC.create(null, EMPTY_RPC_OPTIONS); + fail("Zone cannot be null"); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("entity.managedZone")); + } + // create zone twice + try { + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + fail("Zone already exists."); + } catch (DnsException ex) { + // expected + assertEquals(409, ex.code()); + assertTrue(ex.getMessage().contains("already exists")); + } + // field options + resetProjects(); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "id"); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + resetProjects(); + options.put(DnsRpc.Option.FIELDS, "creationTime"); + zone = RPC.create(ZONE1, options); + assertNotNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "dnsName"); + resetProjects(); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNotNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "description"); + resetProjects(); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "nameServers"); + resetProjects(); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNotNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "nameServerSet"); + resetProjects(); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNull(zone.getId()); + // several combined + options.put(DnsRpc.Option.FIELDS, "nameServerSet,description,id,name"); + resetProjects(); + zone = RPC.create(ZONE1, options); + assertNull(zone.getCreationTime()); + assertNotNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + } + + @Test + public void testGetZone() { + // non-existent + assertNull(RPC.getZone(ZONE_NAME1, EMPTY_RPC_OPTIONS)); + // existent + ManagedZone created = RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + ManagedZone zone = RPC.getZone(ZONE_NAME1, EMPTY_RPC_OPTIONS); + assertEquals(created, zone); + assertEquals(ZONE1.getName(), zone.getName()); + // field options + Map options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "id"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "creationTime"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNotNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "dnsName"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNotNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "description"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "nameServers"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNotNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "nameServerSet"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNull(zone.getId()); + // several combined + options.put(DnsRpc.Option.FIELDS, "nameServerSet,description,id,name"); + zone = RPC.getZone(ZONE1.getName(), options); + assertNull(zone.getCreationTime()); + assertNotNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + } + + @Test + public void testDeleteZone() { + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + assertTrue(RPC.deleteZone(ZONE1.getName())); + assertNull(RPC.getZone(ZONE1.getName(), EMPTY_RPC_OPTIONS)); + // deleting non-existent zone + assertFalse(RPC.deleteZone(ZONE1.getName())); + assertNull(RPC.getZone(ZONE1.getName(), EMPTY_RPC_OPTIONS)); + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + RPC.create(ZONE2, EMPTY_RPC_OPTIONS); + assertNotNull(RPC.getZone(ZONE1.getName(), EMPTY_RPC_OPTIONS)); + assertNotNull(RPC.getZone(ZONE2.getName(), EMPTY_RPC_OPTIONS)); + // delete in reverse order + assertTrue(RPC.deleteZone(ZONE1.getName())); + assertNull(RPC.getZone(ZONE1.getName(), EMPTY_RPC_OPTIONS)); + assertNotNull(RPC.getZone(ZONE2.getName(), EMPTY_RPC_OPTIONS)); + assertTrue(RPC.deleteZone(ZONE2.getName())); + assertNull(RPC.getZone(ZONE1.getName(), EMPTY_RPC_OPTIONS)); + assertNull(RPC.getZone(ZONE2.getName(), EMPTY_RPC_OPTIONS)); + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + try { + RPC.deleteZone(ZONE1.getName()); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("not empty")); + } + } + + @Test + public void testCreateAndApplyChange() { + executeCreateAndApplyChangeTest(RPC); + } + + @Test + public void testCreateAndApplyChangeWithThreads() { + LocalDnsHelper localDnsThreaded = LocalDnsHelper.create(50L); + localDnsThreaded.start(); + DnsRpc rpc = new DefaultDnsRpc(localDnsThreaded.options()); + executeCreateAndApplyChangeTest(rpc); + localDnsThreaded.stop(); + } + + private static void waitForChangeToComplete(DnsRpc rpc, String zoneName, String changeId) { + while (true) { + Change change = rpc.getChangeRequest(zoneName, changeId, EMPTY_RPC_OPTIONS); + if ("done".equals(change.getStatus())) { + return; + } + try { + Thread.sleep(50L); + } catch (InterruptedException e) { + fail("Thread was interrupted while waiting for change processing."); + } + } + } + + private static void executeCreateAndApplyChangeTest(DnsRpc rpc) { + rpc.create(ZONE1, EMPTY_RPC_OPTIONS); + assertNull(rpc.getChangeRequest(ZONE1.getName(), "1", EMPTY_RPC_OPTIONS)); + // add + Change createdChange = rpc.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + assertEquals(CHANGE1.getAdditions(), createdChange.getAdditions()); + assertEquals(CHANGE1.getDeletions(), createdChange.getDeletions()); + assertNotNull(createdChange.getStartTime()); + assertEquals("1", createdChange.getId()); + waitForChangeToComplete(rpc, ZONE1.getName(), "1"); // necessary for the following to return 409 + try { + rpc.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + fail(); + } catch (DnsException ex) { + assertEquals(409, ex.code()); + assertTrue(ex.getMessage().contains("already exists")); + } + assertNotNull(rpc.getChangeRequest(ZONE1.getName(), "1", EMPTY_RPC_OPTIONS)); + assertNull(rpc.getChangeRequest(ZONE1.getName(), "2", EMPTY_RPC_OPTIONS)); + // delete + rpc.applyChangeRequest(ZONE1.getName(), CHANGE2, EMPTY_RPC_OPTIONS); + assertNotNull(rpc.getChangeRequest(ZONE1.getName(), "1", EMPTY_RPC_OPTIONS)); + assertNotNull(rpc.getChangeRequest(ZONE1.getName(), "2", EMPTY_RPC_OPTIONS)); + waitForChangeToComplete(rpc, ZONE1.getName(), "2"); + rpc.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + waitForChangeToComplete(rpc, ZONE1.getName(), "3"); + Iterable results = + rpc.listRecordSets(ZONE1.getName(), EMPTY_RPC_OPTIONS).results(); + List defaults = ImmutableList.of("SOA", "NS"); + boolean rrsetKeep = false; + boolean rrset1 = false; + for (ResourceRecordSet dnsRecord : results) { + if (dnsRecord.getName().equals(RRSET_KEEP.getName()) + && dnsRecord.getType().equals(RRSET_KEEP.getType())) { + rrsetKeep = true; + } else if (dnsRecord.getName().equals(RRSET1.getName()) + && dnsRecord.getType().equals(RRSET1.getType())) { + rrset1 = true; + } else if (!defaults.contains(dnsRecord.getType())) { + fail(String.format("Record with type %s should not exist", dnsRecord.getType())); + } + } + assertTrue(rrset1); + assertTrue(rrsetKeep); + } + + @Test + public void testGetProject() { + // the projects are automatically created when getProject is called + assertNotNull(LOCAL_DNS_HELPER.getProject(PROJECT_ID1, null)); + assertNotNull(LOCAL_DNS_HELPER.getProject(PROJECT_ID2, null)); + Project project = RPC.getProject(EMPTY_RPC_OPTIONS); + assertNotNull(project.getQuota()); + assertEquals(REAL_PROJECT_ID, project.getId()); + // fields options + Map options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "number"); + project = RPC.getProject(options); + assertNull(project.getId()); + assertNotNull(project.getNumber()); + assertNull(project.getQuota()); + options.put(DnsRpc.Option.FIELDS, "id"); + project = RPC.getProject(options); + assertNotNull(project.getId()); + assertNull(project.getNumber()); + assertNull(project.getQuota()); + options.put(DnsRpc.Option.FIELDS, "quota"); + project = RPC.getProject(options); + assertNull(project.getId()); + assertNull(project.getNumber()); + assertNotNull(project.getQuota()); + } + + @Test + public void testCreateChange() { + // non-existent zone + try { + RPC.applyChangeRequest(ZONE_NAME1, CHANGE1, EMPTY_RPC_OPTIONS); + fail("Zone was not created yet."); + } catch (DnsException ex) { + assertEquals(404, ex.code()); + } + // existent zone + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + assertNull(RPC.getChangeRequest(ZONE_NAME1, "1", EMPTY_RPC_OPTIONS)); + Change created = RPC.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + assertEquals(created, RPC.getChangeRequest(ZONE_NAME1, "1", EMPTY_RPC_OPTIONS)); + // field options + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "additions"); + Change complex = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, options); + assertNotNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "deletions"); + complex = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, options); + assertNull(complex.getAdditions()); + assertNotNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "id"); + complex = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNotNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "startTime"); + complex = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNotNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "status"); + complex = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNotNull(complex.getStatus()); + } + + @Test + public void testGetChange() { + // existent + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + Change created = RPC.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + Change retrieved = RPC.getChangeRequest(ZONE1.getName(), "1", EMPTY_RPC_OPTIONS); + assertEquals(created, retrieved); + // non-existent + assertNull(RPC.getChangeRequest(ZONE1.getName(), "2", EMPTY_RPC_OPTIONS)); + // non-existent zone + try { + RPC.getChangeRequest(ZONE_NAME2, "1", EMPTY_RPC_OPTIONS); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(404, ex.code()); + assertTrue(ex.getMessage().contains("managedZone")); + } + // field options + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + Change change = RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, EMPTY_RPC_OPTIONS); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "additions"); + Change complex = RPC.getChangeRequest(ZONE1.getName(), change.getId(), options); + assertNotNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "deletions"); + complex = RPC.getChangeRequest(ZONE1.getName(), change.getId(), options); + assertNull(complex.getAdditions()); + assertNotNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "id"); + complex = RPC.getChangeRequest(ZONE1.getName(), change.getId(), options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNotNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "startTime"); + complex = RPC.getChangeRequest(ZONE1.getName(), change.getId(), options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNotNull(complex.getStartTime()); + assertNull(complex.getStatus()); + options.put(DnsRpc.Option.FIELDS, "status"); + complex = RPC.getChangeRequest(ZONE1.getName(), change.getId(), options); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNotNull(complex.getStatus()); + } + + @Test + public void testListZones() { + Iterable results = RPC.listZones(EMPTY_RPC_OPTIONS).results(); + ImmutableList zones = ImmutableList.copyOf(results); + assertEquals(0, zones.size()); + // some zones exists + ManagedZone created = RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + results = RPC.listZones(EMPTY_RPC_OPTIONS).results(); + zones = ImmutableList.copyOf(results); + assertEquals(created, zones.get(0)); + assertEquals(1, zones.size()); + created = RPC.create(ZONE2, EMPTY_RPC_OPTIONS); + results = RPC.listZones(EMPTY_RPC_OPTIONS).results(); + zones = ImmutableList.copyOf(results); + assertEquals(2, zones.size()); + assertTrue(zones.contains(created)); + // error in options + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 0); + try { + RPC.listZones(options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, -1); + try { + RPC.listZones(options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + // ok size + options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 335); + results = RPC.listZones(options).results(); + zones = ImmutableList.copyOf(results); + assertEquals(2, zones.size()); + // dns name problems + options = new HashMap<>(); + options.put(DnsRpc.Option.DNS_NAME, "aaa"); + try { + RPC.listZones(options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.dnsName")); + } + // ok name + options = new HashMap<>(); + options.put(DnsRpc.Option.DNS_NAME, "aaaa."); + results = RPC.listZones(options).results(); + zones = ImmutableList.copyOf(results); + assertEquals(0, zones.size()); + // field options + options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "managedZones(id)"); + ManagedZone zone = RPC.listZones(options).results().iterator().next(); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "managedZones(creationTime)"); + zone = RPC.listZones(options).results().iterator().next(); + assertNotNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "managedZones(dnsName)"); + zone = RPC.listZones(options).results().iterator().next(); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNotNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "managedZones(description)"); + zone = RPC.listZones(options).results().iterator().next(); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "managedZones(nameServers)"); + zone = RPC.listZones(options).results().iterator().next(); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNotNull(zone.getNameServers()); + assertNull(zone.getNameServerSet()); + assertNull(zone.getId()); + options.put(DnsRpc.Option.FIELDS, "managedZones(nameServerSet)"); + DnsRpc.ListResult listResult = RPC.listZones(options); + zone = listResult.results().iterator().next(); + assertNull(listResult.pageToken()); + assertNull(zone.getCreationTime()); + assertNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNull(zone.getId()); + // several combined + options.put(DnsRpc.Option.FIELDS, + "managedZones(nameServerSet,description,id,name),nextPageToken"); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + listResult = RPC.listZones(options); + zone = listResult.results().iterator().next(); + assertNull(zone.getCreationTime()); + assertNotNull(zone.getName()); + assertNull(zone.getDnsName()); + assertNotNull(zone.getDescription()); + assertNull(zone.getNameServers()); + assertNotNull(zone.getNameServerSet()); + assertNotNull(zone.getId()); + assertEquals(zone.getName(), listResult.pageToken()); + } + + @Test + public void testListDnsRecords() { + // no zone exists + try { + RPC.listRecordSets(ZONE_NAME1, EMPTY_RPC_OPTIONS); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(404, ex.code()); + assertTrue(ex.getMessage().contains("managedZone")); + } + // zone exists but has no records + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + Iterable results = + RPC.listRecordSets(ZONE_NAME1, EMPTY_RPC_OPTIONS).results(); + ImmutableList records = ImmutableList.copyOf(results); + assertEquals(2, records.size()); // contains default NS and SOA + // zone has records + RPC.applyChangeRequest(ZONE_NAME1, CHANGE_KEEP, EMPTY_RPC_OPTIONS); + results = RPC.listRecordSets(ZONE_NAME1, EMPTY_RPC_OPTIONS).results(); + records = ImmutableList.copyOf(results); + assertEquals(3, records.size()); + // error in options + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 0); + try { + RPC.listRecordSets(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + options.put(DnsRpc.Option.PAGE_SIZE, -1); + try { + RPC.listRecordSets(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + options.put(DnsRpc.Option.PAGE_SIZE, 15); + results = RPC.listRecordSets(ZONE1.getName(), options).results(); + records = ImmutableList.copyOf(results); + assertEquals(3, records.size()); + // dnsName filter + options = new HashMap<>(); + options.put(DnsRpc.Option.NAME, "aaa"); + try { + RPC.listRecordSets(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.name")); + } + options.put(DnsRpc.Option.NAME, "aaa."); + results = RPC.listRecordSets(ZONE1.getName(), options).results(); + records = ImmutableList.copyOf(results); + assertEquals(0, records.size()); + options.put(DnsRpc.Option.NAME, null); + options.put(DnsRpc.Option.DNS_TYPE, "A"); + try { + RPC.listRecordSets(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.name")); + } + options.put(DnsRpc.Option.NAME, "aaa."); + options.put(DnsRpc.Option.DNS_TYPE, "a"); + try { + RPC.listRecordSets(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.type")); + } + options.put(DnsRpc.Option.NAME, DNS_NAME); + options.put(DnsRpc.Option.DNS_TYPE, "SOA"); + results = RPC.listRecordSets(ZONE1.getName(), options).results(); + records = ImmutableList.copyOf(results); + assertEquals(1, records.size()); + // field options + options = new HashMap<>(); + options.put(DnsRpc.Option.FIELDS, "rrsets(name)"); + DnsRpc.ListResult listResult = + RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + ResourceRecordSet record = records.get(0); + assertNotNull(record.getName()); + assertNull(record.getRrdatas()); + assertNull(record.getType()); + assertNull(record.getTtl()); + assertNull(listResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "rrsets(rrdatas)"); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + record = records.get(0); + assertNull(record.getName()); + assertNotNull(record.getRrdatas()); + assertNull(record.getType()); + assertNull(record.getTtl()); + assertNull(listResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "rrsets(ttl)"); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + record = records.get(0); + assertNull(record.getName()); + assertNull(record.getRrdatas()); + assertNull(record.getType()); + assertNotNull(record.getTtl()); + assertNull(listResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "rrsets(type)"); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + record = records.get(0); + assertNull(record.getName()); + assertNull(record.getRrdatas()); + assertNotNull(record.getType()); + assertNull(record.getTtl()); + assertNull(listResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "nextPageToken"); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + record = records.get(0); + assertNull(record.getName()); + assertNull(record.getRrdatas()); + assertNull(record.getType()); + assertNull(record.getTtl()); + assertNull(listResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "nextPageToken,rrsets(name,rrdatas)"); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + assertEquals(1, records.size()); + record = records.get(0); + assertNotNull(record.getName()); + assertNotNull(record.getRrdatas()); + assertNull(record.getType()); + assertNull(record.getTtl()); + assertNotNull(listResult.pageToken()); + } + + @Test + public void testListChanges() { + // no such zone exists + try { + RPC.listChangeRequests(ZONE_NAME1, EMPTY_RPC_OPTIONS); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(404, ex.code()); + assertTrue(ex.getMessage().contains("managedZone")); + } + // zone exists but has no changes + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + Iterable results = RPC.listChangeRequests(ZONE1.getName(), EMPTY_RPC_OPTIONS).results(); + ImmutableList changes = ImmutableList.copyOf(results); + assertEquals(1, changes.size()); + // zone has changes + RPC.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE2, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + results = RPC.listChangeRequests(ZONE1.getName(), EMPTY_RPC_OPTIONS).results(); + changes = ImmutableList.copyOf(results); + assertEquals(4, changes.size()); + // error in options + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 0); + try { + RPC.listChangeRequests(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + options.put(DnsRpc.Option.PAGE_SIZE, -1); + try { + RPC.listChangeRequests(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.maxResults")); + } + options.put(DnsRpc.Option.PAGE_SIZE, 15); + results = RPC.listChangeRequests(ZONE1.getName(), options).results(); + changes = ImmutableList.copyOf(results); + assertEquals(4, changes.size()); + options = new HashMap<>(); + options.put(DnsRpc.Option.SORTING_ORDER, "descending"); + results = RPC.listChangeRequests(ZONE1.getName(), options).results(); + ImmutableList descending = ImmutableList.copyOf(results); + results = RPC.listChangeRequests(ZONE1.getName(), EMPTY_RPC_OPTIONS).results(); + ImmutableList ascending = ImmutableList.copyOf(results); + int size = 4; + assertEquals(size, descending.size()); + for (int i = 0; i < size; i++) { + assertEquals(descending.get(i), ascending.get(size - i - 1)); + } + options.put(DnsRpc.Option.SORTING_ORDER, "something else"); + try { + RPC.listChangeRequests(ZONE1.getName(), options); + fail(); + } catch (DnsException ex) { + // expected + assertEquals(400, ex.code()); + assertTrue(ex.getMessage().contains("parameters.sortOrder")); + } + // field options + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_COMPLEX, EMPTY_RPC_OPTIONS); + options = new HashMap<>(); + options.put(DnsRpc.Option.SORTING_ORDER, "descending"); + options.put(DnsRpc.Option.FIELDS, "changes(additions)"); + DnsRpc.ListResult changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + Change complex = changes.get(0); + assertNotNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + assertNull(changeListResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "changes(deletions)"); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + complex = changes.get(0); + assertNull(complex.getAdditions()); + assertNotNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + assertNull(changeListResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "changes(id)"); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + complex = changes.get(0); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNotNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + assertNull(changeListResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "changes(startTime)"); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + complex = changes.get(0); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNotNull(complex.getStartTime()); + assertNull(complex.getStatus()); + assertNull(changeListResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "changes(status)"); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + complex = changes.get(0); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNotNull(complex.getStatus()); + assertNull(changeListResult.pageToken()); + options.put(DnsRpc.Option.FIELDS, "nextPageToken"); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + complex = changes.get(0); + assertNull(complex.getAdditions()); + assertNull(complex.getDeletions()); + assertNull(complex.getId()); + assertNull(complex.getStartTime()); + assertNull(complex.getStatus()); + assertNotNull(changeListResult.pageToken()); + } + + @Test + public void testDnsRecordPaging() { + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + List complete = ImmutableList.copyOf( + RPC.listRecordSets(ZONE1.getName(), EMPTY_RPC_OPTIONS).results()); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + DnsRpc.ListResult listResult = RPC.listRecordSets(ZONE1.getName(), options); + ImmutableList records = ImmutableList.copyOf(listResult.results()); + assertEquals(1, records.size()); + assertEquals(complete.get(0), records.get(0)); + options.put(DnsRpc.Option.PAGE_TOKEN, listResult.pageToken()); + listResult = RPC.listRecordSets(ZONE1.getName(), options); + records = ImmutableList.copyOf(listResult.results()); + assertEquals(1, records.size()); + assertEquals(complete.get(1), records.get(0)); + } + + @Test + public void testZonePaging() { + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + RPC.create(ZONE2, EMPTY_RPC_OPTIONS); + ImmutableList complete = ImmutableList.copyOf( + RPC.listZones(EMPTY_RPC_OPTIONS).results()); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + DnsRpc.ListResult listResult = RPC.listZones(options); + ImmutableList page1 = ImmutableList.copyOf(listResult.results()); + assertEquals(1, page1.size()); + assertEquals(complete.get(0), page1.get(0)); + assertEquals(page1.get(0).getName(), listResult.pageToken()); + options.put(DnsRpc.Option.PAGE_TOKEN, listResult.pageToken()); + listResult = RPC.listZones(options); + ImmutableList page2 = ImmutableList.copyOf(listResult.results()); + assertEquals(1, page2.size()); + assertEquals(complete.get(1), page2.get(0)); + assertNull(listResult.pageToken()); + } + + @Test + public void testChangePaging() { + RPC.create(ZONE1, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE1, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE2, EMPTY_RPC_OPTIONS); + RPC.applyChangeRequest(ZONE1.getName(), CHANGE_KEEP, EMPTY_RPC_OPTIONS); + ImmutableList complete = + ImmutableList.copyOf(RPC.listChangeRequests(ZONE1.getName(), EMPTY_RPC_OPTIONS).results()); + Map options = new HashMap<>(); + options.put(DnsRpc.Option.PAGE_SIZE, 1); + DnsRpc.ListResult changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + List changes = ImmutableList.copyOf(changeListResult.results()); + assertEquals(1, changes.size()); + assertEquals(complete.get(0), changes.get(0)); + assertEquals(complete.get(0).getId(), changeListResult.pageToken()); + options.put(DnsRpc.Option.PAGE_TOKEN, changeListResult.pageToken()); + changeListResult = RPC.listChangeRequests(ZONE1.getName(), options); + changes = ImmutableList.copyOf(changeListResult.results()); + assertEquals(1, changes.size()); + assertEquals(complete.get(1), changes.get(0)); + assertEquals(complete.get(1).getId(), changeListResult.pageToken()); + } + + @Test + public void testToListResponse() { + LocalDnsHelper.Response response = LocalDnsHelper.toListResponse( + Lists.newArrayList("some", "multiple", "words"), "contextA", "IncludeThisPageToken", true); + assertTrue(response.body().contains("IncludeThisPageToken")); + assertTrue(response.body().contains("contextA")); + response = LocalDnsHelper.toListResponse( + Lists.newArrayList("some", "multiple", "words"), "contextB", "IncludeThisPageToken", false); + assertFalse(response.body().contains("IncludeThisPageToken")); + assertTrue(response.body().contains("contextB")); + response = LocalDnsHelper.toListResponse( + Lists.newArrayList("some", "multiple", "words"), "contextC", null, true); + assertFalse(response.body().contains("pageToken")); + assertTrue(response.body().contains("contextC")); + } + + @Test + public void testCreateZoneValidation() { + ManagedZone minimalZone = copyZone(ZONE1); + // no name + ManagedZone copy = copyZone(minimalZone); + copy.setName(null); + LocalDnsHelper.Response response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.name")); + // no description + copy = copyZone(minimalZone); + copy.setDescription(null); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.description")); + // no dns name + copy = copyZone(minimalZone); + copy.setDnsName(null); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.dnsName")); + // zone name does not start with a letter + copy = copyZone(minimalZone); + copy.setName("1aaaaaa"); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.name")); + assertTrue(response.body().contains("Invalid")); + // zone name is too long + copy = copyZone(minimalZone); + copy.setName("123456aaaa123456aaaa123456aaaa123456aaaa123456aaaa123456aaaa123456aaaa123456aa"); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.name")); + assertTrue(response.body().contains("Invalid")); + // zone name contains invalid characters + copy = copyZone(minimalZone); + copy.setName("x1234AA6aa"); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.name")); + assertTrue(response.body().contains("Invalid")); + // zone name contains invalid characters + copy = copyZone(minimalZone); + copy.setName("x a"); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.name")); + assertTrue(response.body().contains("Invalid")); + // dns name does not end with period + copy = copyZone(minimalZone); + copy.setDnsName("aaaaaa.com"); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("entity.managedZone.dnsName")); + assertTrue(response.body().contains("Invalid")); + // dns name is reserved + copy = copyZone(minimalZone); + copy.setDnsName("com."); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(400, response.code()); + assertTrue(response.body().contains("not available to be created.")); + // empty description should pass + copy = copyZone(minimalZone); + copy.setDescription(""); + response = LOCAL_DNS_HELPER.createZone(PROJECT_ID1, copy); + assertEquals(200, response.code()); + } + + @Test + public void testCheckListOptions() { + // listing zones + optionsMap.put("maxResults", "-1"); + LocalDnsHelper.Response response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.maxResults")); + optionsMap.put("maxResults", "0"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.maxResults")); + optionsMap.put("maxResults", "aaaa"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("integer")); + optionsMap.put("maxResults", "15"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("dnsName", "aaa"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.dnsName")); + optionsMap.put("dnsName", "aaa."); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + // listing dns records + optionsMap.put("name", "aaa"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.name")); + optionsMap.put("name", "aaa."); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("name", null); + optionsMap.put("type", "A"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.name")); + optionsMap.put("name", "aaa."); + optionsMap.put("type", "a"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.type")); + optionsMap.put("name", "aaaa."); + optionsMap.put("type", "A"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + // listing changes + optionsMap.put("sortBy", "changeSequence"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("sortBy", "something else"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("Allowed values: [changesequence]")); + optionsMap.put("sortBy", "ChAnGeSeQuEnCe"); // is not case sensitive + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("sortOrder", "ascending"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("sortOrder", "descending"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertNull(response); + optionsMap.put("sortOrder", "somethingelse"); + response = LocalDnsHelper.checkListOptions(optionsMap); + assertEquals(400, response.code()); + assertTrue(response.body().contains("parameters.sortOrder")); + } + + @Test + public void testCheckRrset() { + ResourceRecordSet valid = new ResourceRecordSet(); + valid.setName(ZONE1.getDnsName()); + valid.setType("A"); + valid.setRrdatas(ImmutableList.of("0.255.1.5")); + valid.setTtl(500); + Change validChange = new Change(); + validChange.setAdditions(ImmutableList.of(valid)); + LOCAL_DNS_HELPER.createZone(PROJECT_ID1, ZONE1); + LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, validChange); + // delete with field mismatch + LocalDnsHelper.ZoneContainer zone = LOCAL_DNS_HELPER.findZone(PROJECT_ID1, ZONE_NAME1); + valid.setTtl(valid.getTtl() + 20); + LocalDnsHelper.Response response = LocalDnsHelper.checkRrset(valid, zone, 0, "deletions"); + assertEquals(412, response.code()); + assertTrue(response.body().contains("entity.change.deletions[0]")); + } + + @Test + public void testCheckRrdata() { + // A + assertTrue(LocalDnsHelper.checkRrData("255.255.255.255", "A")); + assertTrue(LocalDnsHelper.checkRrData("13.15.145.165", "A")); + assertTrue(LocalDnsHelper.checkRrData("0.0.0.0", "A")); + assertFalse(LocalDnsHelper.checkRrData("255.255.255.256", "A")); + assertFalse(LocalDnsHelper.checkRrData("-1.255.255.255", "A")); + assertFalse(LocalDnsHelper.checkRrData(".255.255.254", "A")); + assertFalse(LocalDnsHelper.checkRrData("111.255.255.", "A")); + assertFalse(LocalDnsHelper.checkRrData("111.255..22", "A")); + assertFalse(LocalDnsHelper.checkRrData("111.255.aa.22", "A")); + assertFalse(LocalDnsHelper.checkRrData("", "A")); + assertFalse(LocalDnsHelper.checkRrData("...", "A")); + assertFalse(LocalDnsHelper.checkRrData("111.255.12", "A")); + assertFalse(LocalDnsHelper.checkRrData("111.255.12.11.11", "A")); + // AAAA + assertTrue(LocalDnsHelper.checkRrData("1F:fa:09fd::343:aaaa:AAAA:0", "AAAA")); + assertTrue(LocalDnsHelper.checkRrData("0000:FFFF:09fd::343:aaaa:AAAA:0", "AAAA")); + assertFalse(LocalDnsHelper.checkRrData("-2:::::::", "AAAA")); + assertTrue(LocalDnsHelper.checkRrData("0::0", "AAAA")); + assertFalse(LocalDnsHelper.checkRrData("::1FFFF:::::", "AAAA")); + assertFalse(LocalDnsHelper.checkRrData("::aqaa:::::", "AAAA")); + assertFalse(LocalDnsHelper.checkRrData("::::::::", "AAAA")); // too long + assertFalse(LocalDnsHelper.checkRrData("::::::", "AAAA")); // too short + } + + @Test + public void testCheckChange() { + ResourceRecordSet validA = new ResourceRecordSet(); + validA.setName(ZONE1.getDnsName()); + validA.setType("A"); + validA.setRrdatas(ImmutableList.of("0.255.1.5")); + ResourceRecordSet invalidA = new ResourceRecordSet(); + invalidA.setName(ZONE1.getDnsName()); + invalidA.setType("A"); + invalidA.setRrdatas(ImmutableList.of("0.-255.1.5")); + Change validChange = new Change(); + validChange.setAdditions(ImmutableList.of(validA)); + Change invalidChange = new Change(); + invalidChange.setAdditions(ImmutableList.of(invalidA)); + LocalDnsHelper.ZoneContainer zoneContainer = new LocalDnsHelper.ZoneContainer(ZONE1); + LocalDnsHelper.Response response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertNull(response); + response = LocalDnsHelper.checkChange(invalidChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].rrdata[0]")); + // only empty additions/deletions + Change empty = new Change(); + empty.setAdditions(ImmutableList.of()); + empty.setDeletions(ImmutableList.of()); + response = LocalDnsHelper.checkChange(empty, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "The 'entity.change' parameter is required but was missing.")); + // null additions/deletions + empty = new Change(); + response = LocalDnsHelper.checkChange(empty, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "The 'entity.change' parameter is required but was missing.")); + // non-matching name + validA.setName(ZONE1.getDnsName() + ".aaa."); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].name")); + // wrong type + validA.setName(ZONE1.getDnsName()); // revert + validA.setType("ABCD"); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].type")); + // wrong ttl + validA.setType("A"); // revert + validA.setTtl(-1); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].ttl")); + validA.setTtl(null); + // null name + validA.setName(null); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].name")); + validA.setName(ZONE1.getDnsName()); + // null type + validA.setType(null); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].type")); + validA.setType("A"); + // null rrdata + final List temp = validA.getRrdatas(); // preserve + validA.setRrdatas(null); + response = LocalDnsHelper.checkChange(validChange, zoneContainer); + assertEquals(400, response.code()); + assertTrue(response.body().contains("additions[0].rrdata")); + validA.setRrdatas(temp); + // delete non-existent + ResourceRecordSet nonExistent = new ResourceRecordSet(); + nonExistent.setName(ZONE1.getDnsName()); + nonExistent.setType("AAAA"); + nonExistent.setRrdatas(ImmutableList.of("0:0:0:0:5::6")); + Change delete = new Change(); + delete.setDeletions(ImmutableList.of(nonExistent)); + response = LocalDnsHelper.checkChange(delete, zoneContainer); + assertEquals(404, response.code()); + assertTrue(response.body().contains("deletions[0]")); + } + + @Test + public void testCheckAdditionsDeletions() { + ResourceRecordSet validA = new ResourceRecordSet(); + validA.setName(ZONE1.getDnsName()); + validA.setType("A"); + validA.setRrdatas(ImmutableList.of("0.255.1.5")); + Change validChange = new Change(); + validChange.setAdditions(ImmutableList.of(validA)); + LOCAL_DNS_HELPER.createZone(PROJECT_ID1, ZONE1); + LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, validChange); + LocalDnsHelper.ZoneContainer container = LOCAL_DNS_HELPER.findZone(PROJECT_ID1, ZONE_NAME1); + LocalDnsHelper.Response response = + LocalDnsHelper.checkAdditionsDeletions(ImmutableList.of(validA), null, container); + assertEquals(409, response.code()); + assertTrue(response.body().contains("already exists")); + } + + @Test + public void testCreateChangeContentValidation() { + ResourceRecordSet validA = new ResourceRecordSet(); + validA.setName(ZONE1.getDnsName()); + validA.setType("A"); + validA.setRrdatas(ImmutableList.of("0.255.1.5")); + Change validChange = new Change(); + validChange.setAdditions(ImmutableList.of(validA)); + LOCAL_DNS_HELPER.createZone(PROJECT_ID1, ZONE1); + LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, validChange); + LocalDnsHelper.Response response = + LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, validChange); + assertEquals(409, response.code()); + assertTrue(response.body().contains("already exists")); + // delete with field mismatch + Change delete = new Change(); + validA.setTtl(20); // mismatch + delete.setDeletions(ImmutableList.of(validA)); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, delete); + assertEquals(412, response.code()); + assertTrue(response.body().contains("entity.change.deletions[0]")); + // delete and add SOA + Change addition = new Change(); + ImmutableCollection dnsRecords = + LOCAL_DNS_HELPER.findZone(PROJECT_ID1, ZONE_NAME1).dnsRecords().get().values(); + LinkedList deletions = new LinkedList<>(); + LinkedList additions = new LinkedList<>(); + for (ResourceRecordSet rrset : dnsRecords) { + if (rrset.getType().equals("SOA")) { + deletions.add(rrset); + ResourceRecordSet copy = copyRrset(rrset); + copy.setName("x." + copy.getName()); + additions.add(copy); + break; + } + } + delete.setDeletions(deletions); + addition.setAdditions(additions); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, delete); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "zone must contain exactly one resource record set of type 'SOA' at the apex")); + assertTrue(response.body().contains("deletions[0]")); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, addition); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "zone must contain exactly one resource record set of type 'SOA' at the apex")); + assertTrue(response.body().contains("additions[0]")); + // delete NS + deletions = new LinkedList<>(); + additions = new LinkedList<>(); + for (ResourceRecordSet rrset : dnsRecords) { + if (rrset.getType().equals("NS")) { + deletions.add(rrset); + ResourceRecordSet copy = copyRrset(rrset); + copy.setName("x." + copy.getName()); + additions.add(copy); + break; + } + } + delete.setDeletions(deletions); + addition.setAdditions(additions); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, delete); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "zone must contain exactly one resource record set of type 'NS' at the apex")); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, addition); + assertEquals(400, response.code()); + assertTrue(response.body().contains( + "zone must contain exactly one resource record set of type 'NS' at the apex")); + assertTrue(response.body().contains("additions[0]")); + // change (delete + add) + addition.setDeletions(deletions); + response = LOCAL_DNS_HELPER.createChange(PROJECT_ID1, ZONE_NAME1, addition); + assertEquals(200, response.code()); + } + + @Test + public void testMatchesCriteria() { + assertTrue(LocalDnsHelper.matchesCriteria(RRSET1, RRSET1.getName(), RRSET1.getType())); + assertFalse(LocalDnsHelper.matchesCriteria(RRSET1, RRSET1.getName(), "anothertype")); + assertTrue(LocalDnsHelper.matchesCriteria(RRSET1, null, RRSET1.getType())); + assertTrue(LocalDnsHelper.matchesCriteria(RRSET1, RRSET1.getName(), null)); + assertFalse(LocalDnsHelper.matchesCriteria(RRSET1, "anothername", RRSET1.getType())); + } + + @Test + public void testGetUniqueId() { + assertNotNull(LocalDnsHelper.getUniqueId(new HashSet())); + } + + @Test + public void testRandomNameServers() { + assertEquals(4, LocalDnsHelper.randomNameservers().size()); + } + + private static ManagedZone copyZone(ManagedZone original) { + ManagedZone copy = new ManagedZone(); + copy.setDescription(original.getDescription()); + copy.setName(original.getName()); + copy.setCreationTime(original.getCreationTime()); + copy.setId(original.getId()); + copy.setNameServerSet(original.getNameServerSet()); + copy.setDnsName(original.getDnsName()); + if (original.getNameServers() != null) { + copy.setNameServers(ImmutableList.copyOf(original.getNameServers())); + } + return copy; + } + + private static ResourceRecordSet copyRrset(ResourceRecordSet set) { + ResourceRecordSet copy = new ResourceRecordSet(); + if (set.getRrdatas() != null) { + copy.setRrdatas(ImmutableList.copyOf(set.getRrdatas())); + } + copy.setTtl(set.getTtl()); + copy.setName(set.getName()); + copy.setType(set.getType()); + return copy; + } +} diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index 05149442273e..5d610014d8c0 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -5,30 +5,30 @@ Examples for gcloud-java (Java idiomatic client for [Google Cloud Platform][clou [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-examples.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-examples.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-examples.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-examples.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [Examples] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/examples/package-summary.html) +- [Examples] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/examples/package-summary.html) Quickstart ---------- If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-examples - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-examples:0.1.5' +compile 'com.google.cloud:gcloud-java-examples:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-examples" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-examples" % "0.2.0" ``` To run examples from your command line: @@ -63,12 +63,27 @@ To run examples from your command line: ``` * Here's an example run of `DatastoreExample`. - + Be sure to change the placeholder project ID "your-project-id" with your own project ID. Also note that you have to enable the Google Cloud Datastore API on the [Google Developers Console][developers-console] before running the following commands. ``` target/appassembler/bin/DatastoreExample your-project-id my_name add my\ comment target/appassembler/bin/DatastoreExample your-project-id my_name display target/appassembler/bin/DatastoreExample your-project-id my_name delete + + * Here's an example run of `DnsExample`. + + Note that you have to enable the Google Cloud DNS API on the [Google Developers Console][developers-console] before running the following commands. + You will need to replace the domain name `elaborateexample.com` with your own domain name with [verified ownership] (https://www.google.com/webmasters/verification/home). + Also, note that the example creates and deletes record sets of type A only. Operations with other record types are not implemented in the example. + ``` + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="create some-sample-zone elaborateexample.com. description" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list some-sample-zone records" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="add-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="get some-sample-zone" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="delete-record some-sample-zone www.elaborateexample.com. 12.13.14.15 69" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="list some-sample-zone changes ascending" + mvn exec:java -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" -Dexec.args="delete some-sample-zone" ``` * Here's an example run of `ResourceManagerExample`. diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml index fb77f1fc3541..2961df05a356 100644 --- a/gcloud-java-examples/pom.xml +++ b/gcloud-java-examples/pom.xml @@ -4,13 +4,14 @@ gcloud-java-examples jar GCloud Java examples + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-examples Examples for gcloud-java. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-examples diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java similarity index 93% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java index 6555fbdc7ced..38d898f22b6b 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java @@ -14,36 +14,36 @@ * limitations under the License. */ -package com.google.gcloud.examples.bigquery; - +package com.google.cloud.examples.bigquery; + +import com.google.cloud.WriteChannel; +import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryError; +import com.google.cloud.bigquery.BigQueryOptions; +import com.google.cloud.bigquery.CopyJobConfiguration; +import com.google.cloud.bigquery.Dataset; +import com.google.cloud.bigquery.DatasetId; +import com.google.cloud.bigquery.DatasetInfo; +import com.google.cloud.bigquery.ExternalTableDefinition; +import com.google.cloud.bigquery.ExtractJobConfiguration; +import com.google.cloud.bigquery.Field; +import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.FormatOptions; +import com.google.cloud.bigquery.Job; +import com.google.cloud.bigquery.JobId; +import com.google.cloud.bigquery.JobInfo; +import com.google.cloud.bigquery.LoadJobConfiguration; +import com.google.cloud.bigquery.QueryRequest; +import com.google.cloud.bigquery.QueryResponse; +import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardTableDefinition; +import com.google.cloud.bigquery.Table; +import com.google.cloud.bigquery.TableId; +import com.google.cloud.bigquery.TableInfo; +import com.google.cloud.bigquery.ViewDefinition; +import com.google.cloud.bigquery.WriteChannelConfiguration; +import com.google.cloud.bigquery.spi.BigQueryRpc.Tuple; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.bigquery.BigQuery; -import com.google.gcloud.bigquery.BigQueryError; -import com.google.gcloud.bigquery.BigQueryOptions; -import com.google.gcloud.bigquery.CopyJobConfiguration; -import com.google.gcloud.bigquery.Dataset; -import com.google.gcloud.bigquery.DatasetId; -import com.google.gcloud.bigquery.DatasetInfo; -import com.google.gcloud.bigquery.ExternalTableDefinition; -import com.google.gcloud.bigquery.ExtractJobConfiguration; -import com.google.gcloud.bigquery.Field; -import com.google.gcloud.bigquery.FieldValue; -import com.google.gcloud.bigquery.FormatOptions; -import com.google.gcloud.bigquery.Job; -import com.google.gcloud.bigquery.JobId; -import com.google.gcloud.bigquery.JobInfo; -import com.google.gcloud.bigquery.LoadJobConfiguration; -import com.google.gcloud.bigquery.QueryRequest; -import com.google.gcloud.bigquery.QueryResponse; -import com.google.gcloud.bigquery.Schema; -import com.google.gcloud.bigquery.StandardTableDefinition; -import com.google.gcloud.bigquery.Table; -import com.google.gcloud.bigquery.TableId; -import com.google.gcloud.bigquery.TableInfo; -import com.google.gcloud.bigquery.ViewDefinition; -import com.google.gcloud.bigquery.WriteChannelConfiguration; -import com.google.gcloud.bigquery.spi.BigQueryRpc.Tuple; import java.nio.channels.FileChannel; import java.nio.file.Paths; @@ -60,12 +60,20 @@ * *

Steps needed for running the example: *

    +<<<<<<< HEAD:gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/BigQueryExample.java *
  1. log in using gcloud SDK - {@code gcloud auth login}.
  2. *
  3. compile using maven - {@code cd gcloud-java-examples; * mvn package appassembler:assemble -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true} *
  4. *
  5. run - *
    {@code target/appassembler/bin/BigQueryExample []
    +=======
    + * 
  6. login using gcloud SDK - {@code gcloud auth login}.
  7. + *
  8. compile using maven - {@code mvn compile}
  9. + *
  10. run using maven - + *
    {@code mvn exec:java -Dexec.mainClass="com.google.cloud.examples.bigquery.BigQueryExample"
    + *  -Dexec.args="[]
    +>>>>>>> upstream/master:gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java
      *  list datasets |
      *  list tables  |
      *  list jobs |
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/CreateTableAndLoadData.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java
    similarity index 79%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/CreateTableAndLoadData.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java
    index 857f6b43d013..01290ec8b491 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/CreateTableAndLoadData.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java
    @@ -20,18 +20,18 @@
      * the project's READMEs and package-info.java.
      */
     
    -package com.google.gcloud.examples.bigquery.snippets;
    +package com.google.cloud.examples.bigquery.snippets;
     
    -import com.google.gcloud.bigquery.BigQuery;
    -import com.google.gcloud.bigquery.BigQueryOptions;
    -import com.google.gcloud.bigquery.Field;
    -import com.google.gcloud.bigquery.FormatOptions;
    -import com.google.gcloud.bigquery.Job;
    -import com.google.gcloud.bigquery.Schema;
    -import com.google.gcloud.bigquery.StandardTableDefinition;
    -import com.google.gcloud.bigquery.Table;
    -import com.google.gcloud.bigquery.TableId;
    -import com.google.gcloud.bigquery.TableInfo;
    +import com.google.cloud.bigquery.BigQuery;
    +import com.google.cloud.bigquery.BigQueryOptions;
    +import com.google.cloud.bigquery.Field;
    +import com.google.cloud.bigquery.FormatOptions;
    +import com.google.cloud.bigquery.Job;
    +import com.google.cloud.bigquery.Schema;
    +import com.google.cloud.bigquery.StandardTableDefinition;
    +import com.google.cloud.bigquery.Table;
    +import com.google.cloud.bigquery.TableId;
    +import com.google.cloud.bigquery.TableInfo;
     
     /**
      * A snippet for Google Cloud BigQuery showing how to get a BigQuery table or create it if it does
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/InsertDataAndQueryTable.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/InsertDataAndQueryTable.java
    similarity index 81%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/InsertDataAndQueryTable.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/InsertDataAndQueryTable.java
    index f421bc832441..6fb2a7dce997 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/bigquery/snippets/InsertDataAndQueryTable.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/InsertDataAndQueryTable.java
    @@ -20,21 +20,21 @@
      * the project's READMEs and package-info.java.
      */
     
    -package com.google.gcloud.examples.bigquery.snippets;
    +package com.google.cloud.examples.bigquery.snippets;
     
    -import com.google.gcloud.bigquery.BigQuery;
    -import com.google.gcloud.bigquery.BigQueryOptions;
    -import com.google.gcloud.bigquery.DatasetInfo;
    -import com.google.gcloud.bigquery.Field;
    -import com.google.gcloud.bigquery.FieldValue;
    -import com.google.gcloud.bigquery.InsertAllRequest;
    -import com.google.gcloud.bigquery.InsertAllResponse;
    -import com.google.gcloud.bigquery.QueryRequest;
    -import com.google.gcloud.bigquery.QueryResponse;
    -import com.google.gcloud.bigquery.Schema;
    -import com.google.gcloud.bigquery.StandardTableDefinition;
    -import com.google.gcloud.bigquery.TableId;
    -import com.google.gcloud.bigquery.TableInfo;
    +import com.google.cloud.bigquery.BigQuery;
    +import com.google.cloud.bigquery.BigQueryOptions;
    +import com.google.cloud.bigquery.DatasetInfo;
    +import com.google.cloud.bigquery.Field;
    +import com.google.cloud.bigquery.FieldValue;
    +import com.google.cloud.bigquery.InsertAllRequest;
    +import com.google.cloud.bigquery.InsertAllResponse;
    +import com.google.cloud.bigquery.QueryRequest;
    +import com.google.cloud.bigquery.QueryResponse;
    +import com.google.cloud.bigquery.Schema;
    +import com.google.cloud.bigquery.StandardTableDefinition;
    +import com.google.cloud.bigquery.TableId;
    +import com.google.cloud.bigquery.TableInfo;
     
     import java.util.HashMap;
     import java.util.Iterator;
    @@ -84,7 +84,7 @@ public static void main(String... args) throws InterruptedException {
         // Create a query request
         QueryRequest queryRequest = QueryRequest.builder("SELECT * FROM my_dataset_id.my_table_id")
             .maxWaitTime(60000L)
    -        .maxResults(1000L)
    +        .pageSize(1000L)
             .build();
         // Request query to be executed and wait for results
         QueryResponse queryResponse = bigquery.query(queryRequest);
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/DatastoreExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    similarity index 88%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/DatastoreExample.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    index 73f0ebe5bab3..8fc89dff237c 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/DatastoreExample.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/DatastoreExample.java
    @@ -14,21 +14,21 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.examples.datastore;
    +package com.google.cloud.examples.datastore;
     
    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.DateTime;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.FullEntity;
    -import com.google.gcloud.datastore.IncompleteKey;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    -import com.google.gcloud.datastore.Query;
    -import com.google.gcloud.datastore.QueryResults;
    -import com.google.gcloud.datastore.StructuredQuery;
    -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
    -import com.google.gcloud.datastore.Transaction;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.DateTime;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.FullEntity;
    +import com.google.cloud.datastore.IncompleteKey;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
    +import com.google.cloud.datastore.Query;
    +import com.google.cloud.datastore.QueryResults;
    +import com.google.cloud.datastore.StructuredQuery;
    +import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
    +import com.google.cloud.datastore.Transaction;
     
     import java.util.Arrays;
     import java.util.HashMap;
    @@ -183,16 +183,14 @@ public String getRequiredParams() {
     
       public static void main(String... args) {
         String projectId = args.length > 0 ? args[0] : null;
    -    // If you want to access a local Datastore running via the gcd sdk, do
    +    // If you want to access a local Datastore running via the Google Cloud SDK, do
         //   DatastoreOptions options = DatastoreOptions.builder()
         //       .projectId(projectId)
         //       .namespace(NAMESPACE)
    -    //       .host("http://localhost:8080")
    +    //       .host("http://localhost:8080") // change 8080 to the port that the emulator listens to
         //       .build();
    -    DatastoreOptions options = DatastoreOptions.builder()
    -        .projectId(projectId)
    -        .namespace(NAMESPACE)
    -        .build();
    +    DatastoreOptions options =
    +        DatastoreOptions.builder().projectId(projectId).namespace(NAMESPACE).build();
         String name = args.length > 1 ? args[1] : System.getProperty("user.name");
         Datastore datastore = options.service();
         KeyFactory keyFactory = datastore.newKeyFactory().kind(USER_KIND);
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java
    similarity index 85%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java
    index f1e844c79b24..29b8453ccf28 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/AddEntitiesAndRunQuery.java
    @@ -20,16 +20,16 @@
      * the project's READMEs and package-info.java.
      */
     
    -package com.google.gcloud.examples.datastore.snippets;
    +package com.google.cloud.examples.datastore.snippets;
     
    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    -import com.google.gcloud.datastore.Query;
    -import com.google.gcloud.datastore.QueryResults;
    -import com.google.gcloud.datastore.StructuredQuery.PropertyFilter;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
    +import com.google.cloud.datastore.Query;
    +import com.google.cloud.datastore.QueryResults;
    +import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
     
     /**
      * A snippet for Google Cloud Datastore showing how to create and get entities. The snippet also
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/CreateEntity.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/CreateEntity.java
    similarity index 80%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/CreateEntity.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/CreateEntity.java
    index 3981162a2943..0ed4c5081450 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/CreateEntity.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/CreateEntity.java
    @@ -20,14 +20,14 @@
      * the project's READMEs and package-info.java.
      */
     
    -package com.google.gcloud.examples.datastore.snippets;
    +package com.google.cloud.examples.datastore.snippets;
     
    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.DateTime;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.DateTime;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
     
     /**
      * A snippet for Google Cloud Datastore showing how to create an entity.
    diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/UpdateEntity.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/UpdateEntity.java
    similarity index 81%
    rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/UpdateEntity.java
    rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/UpdateEntity.java
    index cbc97f0784dd..79cb6e8b9a5e 100644
    --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/datastore/snippets/UpdateEntity.java
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/datastore/snippets/UpdateEntity.java
    @@ -20,14 +20,14 @@
      * the project's READMEs and package-info.java.
      */
     
    -package com.google.gcloud.examples.datastore.snippets;
    +package com.google.cloud.examples.datastore.snippets;
     
    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.DateTime;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.DateTime;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
     
     /**
      * A snippet for Google Cloud Datastore showing how to get an entity and update it if it exists.
    diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java
    new file mode 100644
    index 000000000000..88b4b984e4c5
    --- /dev/null
    +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/DnsExample.java
    @@ -0,0 +1,526 @@
    +/*
    + * Copyright 2016 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.cloud.examples.dns;
    +
    +import com.google.cloud.dns.ChangeRequest;
    +import com.google.cloud.dns.ChangeRequestInfo;
    +import com.google.cloud.dns.Dns;
    +import com.google.cloud.dns.DnsOptions;
    +import com.google.cloud.dns.ProjectInfo;
    +import com.google.cloud.dns.RecordSet;
    +import com.google.cloud.dns.Zone;
    +import com.google.cloud.dns.ZoneInfo;
    +import com.google.common.base.Joiner;
    +import com.google.common.collect.ImmutableList;
    +
    +import org.joda.time.format.DateTimeFormat;
    +import org.joda.time.format.DateTimeFormatter;
    +
    +import java.util.Arrays;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.Map;
    +import java.util.concurrent.TimeUnit;
    +
    +/**
    + * An example of using Google Cloud DNS.
    + *
    + * 

    This example creates, deletes, gets, and lists zones. It also creates and deletes + * record sets of type A, and lists record sets. + * + *

    Steps needed for running the example: + *

      + *
    1. login using gcloud SDK - {@code gcloud auth login}.
    2. + *
    3. compile using maven - {@code mvn compile}
    4. + *
    5. run using maven - {@code mvn exec:java + * -Dexec.mainClass="com.google.cloud.examples.dns.DnsExample" + * -Dexec.args="[] + * create | + * get | + * delete | + * list [ [changes [descending | ascending] | records]] | + * add-record | + * delete-record [] | + * quota}
    6. + *
    + * + *

    The first parameter is an optional {@code project_id}. The project specified in the Google + * Cloud SDK configuration (see {@code gcloud config list}) will be used if the project ID is not + * supplied. The second parameter is a DNS operation (list, delete, create, ...). The remaining + * arguments are specific to the operation. See each action's {@code run} method for the specific + * arguments. + */ +public class DnsExample { + + private static final Map ACTIONS = new HashMap<>(); + private static final DateTimeFormatter FORMATTER = + DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss"); + + private interface DnsAction { + void run(Dns dns, String... args); + + String params(); + + boolean check(String... args); + } + + private static class CreateZoneAction implements DnsAction { + + /** + * Creates a zone with the provided name, DNS name and description (in this order). + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + String dnsName = args[1]; + String description = args[2]; + ZoneInfo zoneInfo = ZoneInfo.of(zoneName, dnsName, description); + Zone zone = dns.create(zoneInfo); + System.out.printf("Successfully created zone with name %s which was assigned ID %s.%n", + zone.name(), zone.generatedId()); + } + + @Override + public String params() { + return " "; + } + + @Override + public boolean check(String... args) { + return args.length == 3; + } + } + + private static class ListZonesAction implements DnsAction { + + /** + * Lists all zones within the project. + */ + @Override + public void run(Dns dns, String... args) { + Iterator zoneIterator = dns.listZones().iterateAll(); + if (zoneIterator.hasNext()) { + System.out.println("The project contains the following zones:"); + while (zoneIterator.hasNext()) { + printZone(zoneIterator.next()); + } + } else { + System.out.println("Project contains no zones."); + } + } + + @Override + public String params() { + return ""; + } + + @Override + public boolean check(String... args) { + return args.length == 0; + } + } + + private static class GetZoneAction implements DnsAction { + + /** + * Gets details about a zone with the given name. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + Zone zone = dns.getZone(zoneName); + if (zone == null) { + System.out.printf("No zone with name '%s' exists.%n", zoneName); + } else { + printZone(zone); + } + } + + @Override + public String params() { + return ""; + } + + @Override + public boolean check(String... args) { + return args.length == 1; + } + } + + private static class DeleteZoneAction implements DnsAction { + + /** + * Deletes a zone with the given name. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + boolean deleted = dns.delete(zoneName); + if (deleted) { + System.out.printf("Zone %s was deleted.%n", zoneName); + } else { + System.out.printf("Zone %s was NOT deleted. It does not exist.%n", zoneName); + } + } + + @Override + public String params() { + return ""; + } + + @Override + public boolean check(String... args) { + return args.length == 1; + } + + } + + private static class DeleteDnsRecordAction implements DnsAction { + + /** + * Deletes a DNS record of type A from the given zone. The last parameter is ttl and it is not + * required. If ttl is not provided, a default value of 0 is used. The service requires a + * precise match (including ttl) for deleting a record. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + String recordName = args[1]; + String ip = args[2]; + int ttl = 0; + if (args.length > 3) { + ttl = Integer.parseInt(args[3]); + } + RecordSet recordSet = RecordSet.builder(recordName, RecordSet.Type.A) + .records(ImmutableList.of(ip)) + .ttl(ttl, TimeUnit.SECONDS) + .build(); + ChangeRequestInfo changeRequest = ChangeRequest.builder() + .delete(recordSet) + .build(); + changeRequest = dns.applyChangeRequest(zoneName, changeRequest); + System.out.printf("The request for deleting A record %s for zone %s was successfully " + + "submitted and assigned ID %s.%n", recordName, zoneName, changeRequest.generatedId()); + System.out.print("Waiting for deletion to happen..."); + waitForChangeToFinish(dns, zoneName, changeRequest); + System.out.printf("%nThe deletion has been completed.%n"); + } + + @Override + public String params() { + return " []"; + } + + @Override + public boolean check(String... args) { + if (args.length == 4) { + // to check that it can be parsed + Integer.parseInt(args[3]); + return true; + } else { + return args.length == 3; + } + } + } + + private static class AddDnsRecordAction implements DnsAction { + + /** + * Adds a record set of type A. The last parameter is ttl and is not required. If ttl is not + * provided, a default value of 0 will be used. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + String recordName = args[1]; + String ip = args[2]; + int ttl = 0; + if (args.length > 3) { + ttl = Integer.parseInt(args[3]); + } + RecordSet recordSet = RecordSet.builder(recordName, RecordSet.Type.A) + .records(ImmutableList.of(ip)) + .ttl(ttl, TimeUnit.SECONDS) + .build(); + ChangeRequestInfo changeRequest = ChangeRequest.builder().add(recordSet).build(); + changeRequest = dns.applyChangeRequest(zoneName, changeRequest); + System.out.printf("The request for adding A record %s for zone %s was successfully " + + "submitted and assigned ID %s.%n", recordName, zoneName, changeRequest.generatedId()); + System.out.print("Waiting for addition to happen..."); + waitForChangeToFinish(dns, zoneName, changeRequest); + System.out.printf("The addition has been completed.%n"); + } + + @Override + public String params() { + return " []"; + } + + @Override + public boolean check(String... args) { + if (args.length == 4) { + // to check that it can be parsed + Integer.parseInt(args[3]); + return true; + } else { + return args.length == 3; + } + } + } + + private static class ListDnsRecordsAction implements DnsAction { + + /** + * Lists all the record sets in the given zone. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + Iterator iterator = dns.listRecordSets(zoneName).iterateAll(); + if (iterator.hasNext()) { + System.out.printf("Record sets for zone %s:%n", zoneName); + while (iterator.hasNext()) { + RecordSet recordSet = iterator.next(); + System.out.printf("%nRecord name: %s%nTTL: %s%nRecords: %s%n", recordSet.name(), + recordSet.ttl(), Joiner.on(", ").join(recordSet.records())); + } + } else { + System.out.printf("Zone %s has no record sets records.%n", zoneName); + } + } + + @Override + public String params() { + return " records"; + } + + @Override + public boolean check(String... args) { + return args.length == 2; + } + } + + private static class ListChangesAction implements DnsAction { + + /** + * Lists all the changes for a given zone. Optionally, an order ("descending" or "ascending") + * can be specified using the last parameter. + */ + @Override + public void run(Dns dns, String... args) { + String zoneName = args[0]; + Iterator iterator; + if (args.length > 2) { + Dns.SortingOrder sortOrder = Dns.SortingOrder.valueOf(args[2].toUpperCase()); + iterator = dns.listChangeRequests(zoneName, + Dns.ChangeRequestListOption.sortOrder(sortOrder)).iterateAll(); + } else { + iterator = dns.listChangeRequests(zoneName).iterateAll(); + } + if (iterator.hasNext()) { + System.out.printf("Change requests for zone %s:%n", zoneName); + while (iterator.hasNext()) { + ChangeRequest change = iterator.next(); + System.out.printf("%nID: %s%n", change.generatedId()); + System.out.printf("Status: %s%n", change.status()); + System.out.printf("Started: %s%n", FORMATTER.print(change.startTimeMillis())); + System.out.printf("Deletions: %s%n", Joiner.on(", ").join(change.deletions())); + System.out.printf("Additions: %s%n", Joiner.on(", ").join(change.additions())); + } + } else { + System.out.printf("Zone %s has no change requests.%n", zoneName); + } + } + + @Override + public String params() { + return " changes [descending | ascending]"; + } + + @Override + public boolean check(String... args) { + return args.length == 2 + || (args.length == 3 + && ImmutableList.of("descending", "ascending").contains(args[2].toLowerCase())); + } + } + + private static class ListAction implements DnsAction { + + /** + * Invokes a list action. If no parameter is provided, lists all zones. If zone name is the only + * parameter provided, lists both record sets and changes. Otherwise, invokes listing + * changes or zones based on the parameter provided. + */ + @Override + public void run(Dns dns, String... args) { + if (args.length == 0) { + new ListZonesAction().run(dns); + } else { + if (args.length == 1 || "records".equals(args[1])) { + new ListDnsRecordsAction().run(dns, args); + } + if (args.length == 1 || "changes".equals(args[1])) { + new ListChangesAction().run(dns, args); + } + } + } + + @Override + public boolean check(String... args) { + if (args.length == 0 || args.length == 1) { + return true; + } + if ("records".equals(args[1])) { + return new ListDnsRecordsAction().check(args); + } + if ("changes".equals(args[1])) { + return new ListChangesAction().check(args); + } + return false; + } + + @Override + public String params() { + return "[ [changes [descending | ascending] | records]]"; + } + } + + private static class GetProjectAction implements DnsAction { + + @Override + public void run(Dns dns, String... args) { + ProjectInfo project = dns.getProject(); + ProjectInfo.Quota quota = project.quota(); + System.out.printf("Project id: %s%nQuota:%n", dns.options().projectId()); + System.out.printf("\tZones: %d%n", quota.zones()); + System.out.printf("\tRecord sets per zone: %d%n", quota.rrsetsPerZone()); + System.out.printf("\tRecord sets per DNS record: %d%n", + quota.resourceRecordsPerRrset()); + System.out.printf("\tAdditions per change: %d%n", quota.rrsetAdditionsPerChange()); + System.out.printf("\tDeletions per change: %d%n", quota.rrsetDeletionsPerChange()); + System.out.printf("\tTotal data size per change: %d%n", + quota.totalRrdataSizePerChange()); + } + + @Override + public String params() { + return ""; + } + + @Override + public boolean check(String... args) { + return args.length == 0; + } + } + + static { + ACTIONS.put("create", new CreateZoneAction()); + ACTIONS.put("delete", new DeleteZoneAction()); + ACTIONS.put("get", new GetZoneAction()); + ACTIONS.put("list", new ListAction()); + ACTIONS.put("add-record", new AddDnsRecordAction()); + ACTIONS.put("delete-record", new DeleteDnsRecordAction()); + ACTIONS.put("quota", new GetProjectAction()); + } + + private static void printZone(Zone zone) { + System.out.printf("%nName: %s%n", zone.name()); + System.out.printf("ID: %s%n", zone.generatedId()); + System.out.printf("Description: %s%n", zone.description()); + System.out.printf("Created: %s%n", FORMATTER.print(zone.creationTimeMillis())); + System.out.printf("Name servers: %s%n", Joiner.on(", ").join(zone.nameServers())); + } + + private static ChangeRequestInfo waitForChangeToFinish(Dns dns, String zoneName, + ChangeRequestInfo request) { + ChangeRequestInfo current = request; + while (current.status().equals(ChangeRequest.Status.PENDING)) { + System.out.print("."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.err.println("Thread was interrupted while waiting."); + } + current = dns.getChangeRequest(zoneName, current.generatedId()); + } + return current; + } + + private static void printUsage() { + StringBuilder actionAndParams = new StringBuilder(); + for (Map.Entry entry : ACTIONS.entrySet()) { + actionAndParams.append(System.lineSeparator()).append('\t').append(entry.getKey()); + String param = entry.getValue().params(); + if (param != null && !param.isEmpty()) { + actionAndParams.append(' ').append(param); + } + } + System.out.printf("Usage: %s [] operation *%s%n", + DnsExample.class.getSimpleName(), actionAndParams); + } + + public static void main(String... args) throws Exception { + if (args.length < 1) { + System.out.println("Missing required action"); + printUsage(); + return; + } + String projectId = null; + DnsAction action; + String actionName; + if (args.length >= 2 && !ACTIONS.containsKey(args[0])) { + actionName = args[1]; + projectId = args[0]; + args = Arrays.copyOfRange(args, 2, args.length); + } else { + actionName = args[0]; + args = Arrays.copyOfRange(args, 1, args.length); + } + action = ACTIONS.get(actionName); + if (action == null) { + System.out.printf("Unrecognized action %s.%n", actionName); + printUsage(); + return; + } + boolean valid = false; + try { + valid = action.check(args); + } catch (NumberFormatException ex) { + System.out.println("Invalid input for action '" + actionName + "'."); + System.out.println("Ttl must be an integer."); + System.out.println("Expected: " + action.params()); + return; + } catch (Exception ex) { + System.out.println("Failed to parse request."); + System.out.println("Expected: " + action.params()); + ex.printStackTrace(); + return; + } + if (valid) { + DnsOptions.Builder optionsBuilder = DnsOptions.builder(); + if (projectId != null) { + optionsBuilder.projectId(projectId); + } + Dns dns = optionsBuilder.build().service(); + action.run(dns, args); + } else { + System.out.println("Invalid input for action '" + actionName + "'"); + System.out.println("Expected: " + action.params()); + } + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateOrUpdateRecordSets.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateOrUpdateRecordSets.java new file mode 100644 index 000000000000..b49cdeede5fb --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateOrUpdateRecordSets.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016 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. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in README's and javadoc. Any change to this file should be reflected in + * the project's README's and package-info.java. + */ + +package com.google.cloud.examples.dns.snippets; + +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.RecordSet; +import com.google.cloud.dns.Zone; + +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +/** + * A snippet for Google Cloud DNS showing how to create and update a resource record set. + */ +public class CreateOrUpdateRecordSets { + + public static void main(String... args) { + // Create a service object. + // The project ID and credentials will be inferred from the environment. + Dns dns = DnsOptions.defaultInstance().service(); + + // Change this to a zone name that exists within your project + String zoneName = "my-unique-zone"; + + // Get zone from the service + Zone zone = dns.getZone(zoneName); + + // Prepare a www.. type A record set with ttl of 24 hours + String ip = "12.13.14.15"; + RecordSet toCreate = RecordSet.builder("www." + zone.dnsName(), RecordSet.Type.A) + .ttl(24, TimeUnit.HOURS) + .addRecord(ip) + .build(); + + // Make a change + ChangeRequestInfo.Builder changeBuilder = ChangeRequestInfo.builder().add(toCreate); + + // Verify a www.. type A record does not exist yet. + // If it does exist, we will overwrite it with our prepared record. + Iterator recordSetIterator = zone.listRecordSets().iterateAll(); + while (recordSetIterator.hasNext()) { + RecordSet current = recordSetIterator.next(); + if (toCreate.name().equals(current.name()) && toCreate.type().equals(current.type())) { + changeBuilder.delete(current); + } + } + + // Build and apply the change request to our zone + ChangeRequestInfo changeRequest = changeBuilder.build(); + zone.applyChangeRequest(changeRequest); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateZone.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateZone.java new file mode 100644 index 000000000000..d28ad5cbb2c9 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/CreateZone.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 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. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in README's and javadoc. Any change to this file should be reflected in + * the project's README's and package-info.java. + */ + +package com.google.cloud.examples.dns.snippets; + +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.Zone; +import com.google.cloud.dns.ZoneInfo; + +/** + * A snippet for Google Cloud DNS showing how to create a zone. You will need to change the {@code + * domainName} to a domain name, the ownership of which you should verify with Google. + */ +public class CreateZone { + + public static void main(String... args) { + // Create a service object + // The project ID and credentials will be inferred from the environment. + Dns dns = DnsOptions.defaultInstance().service(); + + // Create a zone metadata object + String zoneName = "my-unique-zone"; // Change this zone name which is unique within your project + String domainName = "someexampledomain.com."; // Change this to a domain which you own + String description = "This is a gcloud-java-dns sample zone."; + ZoneInfo zoneInfo = ZoneInfo.of(zoneName, domainName, description); + + // Create zone in Google Cloud DNS + Zone zone = dns.create(zoneInfo); + System.out.printf("Zone was created and assigned ID %s.%n", zone.generatedId()); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/DeleteZone.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/DeleteZone.java new file mode 100644 index 000000000000..317daf0c2e7d --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/DeleteZone.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016 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. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in README's and javadoc. Any change to this file should be reflected in + * the project's README's and package-info.java. + */ + +package com.google.cloud.examples.dns.snippets; + +import com.google.cloud.dns.ChangeRequest; +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.RecordSet; + +import java.util.Iterator; + +/** + * A snippet for Google Cloud DNS showing how to delete a zone. It also shows how to list and delete + * DNS records. + */ +public class DeleteZone { + + public static void main(String... args) { + // Create a service object. + // The project ID and credentials will be inferred from the environment. + Dns dns = DnsOptions.defaultInstance().service(); + + // Change this to a zone name that exists within your project and that you want to delete. + String zoneName = "my-unique-zone"; + + // Get iterator for the existing record sets which have to be deleted before deleting the zone + Iterator recordIterator = dns.listRecordSets(zoneName).iterateAll(); + + // Make a change for deleting the records + ChangeRequestInfo.Builder changeBuilder = ChangeRequestInfo.builder(); + while (recordIterator.hasNext()) { + RecordSet current = recordIterator.next(); + // SOA and NS records cannot be deleted + if (!RecordSet.Type.SOA.equals(current.type()) && !RecordSet.Type.NS.equals(current.type())) { + changeBuilder.delete(current); + } + } + + // Build and apply the change request to our zone if it contains records to delete + ChangeRequestInfo changeRequest = changeBuilder.build(); + if (!changeRequest.deletions().isEmpty()) { + ChangeRequest pendingRequest = dns.applyChangeRequest(zoneName, changeRequest); + + // Wait for the change request to complete + while (!pendingRequest.isDone()) { + System.out.println("Waiting for change to complete. Going to sleep for 500ms..."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.err.println("The thread was interrupted while waiting for change request to be " + + "processed."); + } + } + } + + // Delete the zone + boolean result = dns.delete(zoneName); + if (result) { + System.out.println("Zone was deleted."); + } else { + System.out.println("Zone was not deleted because it does not exist."); + } + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/ManipulateZonesAndRecordSets.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/ManipulateZonesAndRecordSets.java new file mode 100644 index 000000000000..53cd77b5ec67 --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/dns/snippets/ManipulateZonesAndRecordSets.java @@ -0,0 +1,153 @@ +/* + * Copyright 2016 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. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in README's and javadoc. Any change to this file should be reflected in + * the project's README's and package-info.java. + */ + +package com.google.cloud.examples.dns.snippets; + +import com.google.cloud.dns.ChangeRequest; +import com.google.cloud.dns.ChangeRequestInfo; +import com.google.cloud.dns.Dns; +import com.google.cloud.dns.DnsOptions; +import com.google.cloud.dns.RecordSet; +import com.google.cloud.dns.Zone; +import com.google.cloud.dns.ZoneInfo; + +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * A complete snippet for Google Cloud DNS showing how to create and delete a zone. It also shows + * how to create, list and delete record sets, and how to list changes. + */ +public class ManipulateZonesAndRecordSets { + + public static void main(String... args) { + Dns dns = DnsOptions.defaultInstance().service(); + + // Create a zone metadata object + String zoneName = "my-unique-zone"; // Change this zone name which is unique within your project + String domainName = "someexampledomain.com."; // Change this to a domain which you own + String description = "This is a gcloud-java-dns sample zone."; + ZoneInfo zoneInfo = ZoneInfo.of(zoneName, domainName, description); + + // Create zone in Google Cloud DNS + Zone zone = dns.create(zoneInfo); + System.out.printf("Zone was created and assigned ID %s.%n", zone.generatedId()); + + // Print assigned name servers + List nameServers = zone.nameServers(); + for (String nameServer : nameServers) { + System.out.println(nameServer); + } + + // Prepare a www.someexampledomain.com. type A record with ttl of 24 hours + String ip = "12.13.14.15"; + RecordSet toCreate = RecordSet.builder("www.someexampledomain.com.", RecordSet.Type.A) + .ttl(24, TimeUnit.HOURS) + .addRecord(ip) + .build(); + + // Make a change + ChangeRequestInfo.Builder changeBuilder = ChangeRequestInfo.builder().add(toCreate); + + // Verify the type A record does not exist yet. + // If it does exist, we will overwrite it with our prepared record. + Iterator recordSetIterator = zone.listRecordSets().iterateAll(); + while (recordSetIterator.hasNext()) { + RecordSet current = recordSetIterator.next(); + if (toCreate.name().equals(current.name()) && toCreate.type().equals(current.type())) { + changeBuilder.delete(current); + } + } + + // Build and apply the change request to our zone + ChangeRequestInfo changeRequest = changeBuilder.build(); + zone.applyChangeRequest(changeRequest); + + while (ChangeRequestInfo.Status.PENDING.equals(changeRequest.status())) { + try { + Thread.sleep(500L); + } catch (InterruptedException e) { + System.err.println("The thread was interrupted while waiting..."); + } + changeRequest = dns.getChangeRequest(zone.name(), changeRequest.generatedId()); + } + System.out.println("The change request has been applied."); + + // List all your zones + Iterator zoneIterator = dns.listZones().iterateAll(); + int counter = 1; + while (zoneIterator.hasNext()) { + System.out.printf("#%d.: %s%n%n", counter, zoneIterator.next()); + counter++; + } + + // List the record sets in a particular zone + recordSetIterator = zone.listRecordSets().iterateAll(); + System.out.println(String.format("Record sets inside %s:", zone.name())); + while (recordSetIterator.hasNext()) { + System.out.println(recordSetIterator.next()); + } + + // List the change requests applied to a particular zone + Iterator changeIterator = zone.listChangeRequests().iterateAll(); + System.out.println(String.format("The history of changes in %s:", zone.name())); + while (changeIterator.hasNext()) { + System.out.println(changeIterator.next()); + } + + // Make a change for deleting the record sets + changeBuilder = ChangeRequestInfo.builder(); + while (recordSetIterator.hasNext()) { + RecordSet current = recordSetIterator.next(); + // SOA and NS records cannot be deleted + if (!RecordSet.Type.SOA.equals(current.type()) && !RecordSet.Type.NS.equals(current.type())) { + changeBuilder.delete(current); + } + } + + // Build and apply the change request to our zone if it contains records to delete + changeRequest = changeBuilder.build(); + if (!changeRequest.deletions().isEmpty()) { + ChangeRequest pendingRequest = dns.applyChangeRequest(zoneName, changeRequest); + + // Wait for the change request to complete + while (!pendingRequest.isDone()) { + System.out.println("Waiting for change to complete. Going to sleep for 500ms..."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.err.println("The thread was interrupted while waiting for change request to be " + + "processed."); + } + } + } + + // Delete the zone + boolean result = dns.delete(zoneName); + if (result) { + System.out.println("Zone was deleted."); + } else { + System.out.println("Zone was not deleted because it does not exist."); + } + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/ResourceManagerExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java similarity index 96% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/ResourceManagerExample.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java index 0762fc779620..253b4a6086cb 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/ResourceManagerExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.examples.resourcemanager; +package com.google.cloud.examples.resourcemanager; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ProjectInfo; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; import com.google.common.base.Joiner; -import com.google.gcloud.resourcemanager.Project; -import com.google.gcloud.resourcemanager.ProjectInfo; -import com.google.gcloud.resourcemanager.ResourceManager; -import com.google.gcloud.resourcemanager.ResourceManagerOptions; import java.util.Arrays; import java.util.HashMap; diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/GetOrCreateProject.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/GetOrCreateProject.java similarity index 84% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/GetOrCreateProject.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/GetOrCreateProject.java index 5a298107cc60..796e29bc3b0c 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/GetOrCreateProject.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/GetOrCreateProject.java @@ -20,12 +20,12 @@ * the project's READMEs and package-info.java. */ -package com.google.gcloud.examples.resourcemanager.snippets; +package com.google.cloud.examples.resourcemanager.snippets; -import com.google.gcloud.resourcemanager.Project; -import com.google.gcloud.resourcemanager.ProjectInfo; -import com.google.gcloud.resourcemanager.ResourceManager; -import com.google.gcloud.resourcemanager.ResourceManagerOptions; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ProjectInfo; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; /** * A snippet for Google Cloud Resource Manager showing how to create a project if it does not exist. diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/ModifyPolicy.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/ModifyPolicy.java new file mode 100644 index 000000000000..3f12dba0f77f --- /dev/null +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/ModifyPolicy.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016 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. + */ + +/* + * EDITING INSTRUCTIONS + * This file is referenced in READMEs and javadoc. Any change to this file should be reflected in + * the project's READMEs and package-info.java. + */ + +package com.google.cloud.examples.resourcemanager.snippets; + +import com.google.cloud.Identity; +import com.google.cloud.resourcemanager.Policy; +import com.google.cloud.resourcemanager.Policy.ProjectRole; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; + +/** + * A snippet for Google Cloud Resource Manager showing how to modify a project's IAM policy. + */ +public class ModifyPolicy { + + 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(); + + // Get a project from the server + String projectId = "some-project-id"; // Use an existing project's ID + Project project = resourceManager.get(projectId); + + // Get the project's policy + Policy policy = project.getPolicy(); + + // Add a viewer + Policy.Builder modifiedPolicy = policy.toBuilder(); + Identity newViewer = Identity.user(""); + modifiedPolicy.addIdentity(ProjectRole.VIEWER.value(), newViewer); + + // Write policy + Policy updatedPolicy = project.replacePolicy(modifiedPolicy.build()); + + // Print policy + System.out.printf("Updated policy for %s: %n%s%n", projectId, updatedPolicy); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/UpdateAndListProjects.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/UpdateAndListProjects.java similarity index 89% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/UpdateAndListProjects.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/UpdateAndListProjects.java index b194de0815d5..2dba787279f2 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/UpdateAndListProjects.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/UpdateAndListProjects.java @@ -20,11 +20,11 @@ * the project's READMEs and package-info.java. */ -package com.google.gcloud.examples.resourcemanager.snippets; +package com.google.cloud.examples.resourcemanager.snippets; -import com.google.gcloud.resourcemanager.Project; -import com.google.gcloud.resourcemanager.ResourceManager; -import com.google.gcloud.resourcemanager.ResourceManagerOptions; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; import java.util.Iterator; diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java similarity index 95% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/StorageExample.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java index 6ca59e836f58..72ff87376003 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java @@ -14,23 +14,23 @@ * limitations under the License. */ -package com.google.gcloud.examples.storage; - -import com.google.gcloud.AuthCredentials; -import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.BlobInfo; -import com.google.gcloud.storage.Bucket; -import com.google.gcloud.storage.CopyWriter; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.Storage.ComposeRequest; -import com.google.gcloud.storage.Storage.CopyRequest; -import com.google.gcloud.storage.Storage.SignUrlOption; -import com.google.gcloud.storage.StorageOptions; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; +package com.google.cloud.examples.storage; + +import com.google.cloud.AuthCredentials; +import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials; +import com.google.cloud.ReadChannel; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.Storage.ComposeRequest; +import com.google.cloud.storage.Storage.CopyRequest; +import com.google.cloud.storage.Storage.SignUrlOption; +import com.google.cloud.storage.StorageOptions; +import com.google.cloud.storage.spi.StorageRpc.Tuple; import java.io.FileOutputStream; import java.io.IOException; @@ -490,7 +490,7 @@ public void run(Storage storage, Tuple private void run(Storage storage, ServiceAccountAuthCredentials cred, BlobInfo blobInfo) { Blob blob = storage.get(blobInfo.blobId()); System.out.println("Signed URL: " - + blob.signUrl(1, TimeUnit.DAYS, SignUrlOption.serviceAccount(cred))); + + blob.signUrl(1, TimeUnit.DAYS, SignUrlOption.signWith(cred))); } @Override diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java similarity index 89% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java index 435cc90b03d8..922afd7047c3 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java @@ -20,15 +20,15 @@ * the project's READMEs and package-info.java. */ -package com.google.gcloud.examples.storage.snippets; +package com.google.cloud.examples.storage.snippets; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.Bucket; -import com.google.gcloud.storage.BucketInfo; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import java.util.Iterator; diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateBlob.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateBlob.java similarity index 82% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateBlob.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateBlob.java index 2c1304a478ab..afe07b0e5206 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateBlob.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateBlob.java @@ -20,15 +20,15 @@ * the project's READMEs and package-info.java. */ -package com.google.gcloud.examples.storage.snippets; +package com.google.cloud.examples.storage.snippets; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.BlobInfo; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; /** * A snippet for Google Cloud Storage showing how to create a blob. diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/UpdateBlob.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/UpdateBlob.java similarity index 87% rename from gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/UpdateBlob.java rename to gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/UpdateBlob.java index 13290b201787..c645ba1e4a54 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/UpdateBlob.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/UpdateBlob.java @@ -20,14 +20,14 @@ * the project's READMEs and package-info.java. */ -package com.google.gcloud.examples.storage.snippets; +package com.google.cloud.examples.storage.snippets; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/gcloud-java-resourcemanager/README.md b/gcloud-java-resourcemanager/README.md index 94037e27a709..906ef82710d7 100644 --- a/gcloud-java-resourcemanager/README.md +++ b/gcloud-java-resourcemanager/README.md @@ -5,12 +5,12 @@ Java idiomatic client for [Google Cloud Resource Manager] (https://cloud.google. [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-resourcemanager.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-resourcemanager.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-resourcemanager.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-resourcemanager.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/resourcemanager/package-summary.html) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/resourcemanager/package-summary.html) > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. @@ -20,23 +20,23 @@ Quickstart If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-resourcemanager - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-resourcemanager:0.1.5' +compile 'com.google.cloud:gcloud-java-resourcemanager:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-resourcemanager" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-resourcemanager" % "0.2.0" ``` Example Application -------------------- -[`ResourceManagerExample`](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/ResourceManagerExample.java) is a simple command line interface for the Cloud Resource Manager. Read more about using the application on the [`ResourceManagerExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/resourcemanager/ResourceManagerExample.html). +[`ResourceManagerExample`](../gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/ResourceManagerExample.java) is a simple command line interface for the Cloud Resource Manager. Read more about using the application on the [`ResourceManagerExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/resourcemanager/ResourceManagerExample.html). Authentication -------------- @@ -80,8 +80,8 @@ These credentials are automatically inferred from your environment, so you only code to create your service object: ```java -import com.google.gcloud.resourcemanager.ResourceManager; -import com.google.gcloud.resourcemanager.ResourceManagerOptions; +import com.google.cloud.resourcemanager.ResourceManager; +import com.google.cloud.resourcemanager.ResourceManagerOptions; ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service(); ``` @@ -91,7 +91,7 @@ You can load a project if you know it's project ID and have read permissions to To get a project, add the following import at the top of your file: ```java -import com.google.gcloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.Project; ``` Then use the following code to get the project: @@ -108,8 +108,8 @@ names, and labels [here](https://cloud.google.com/resource-manager/reference/res To create a project, add the following imports at the top of your file: ```java -import com.google.gcloud.resourcemanager.Project; -import com.google.gcloud.resourcemanager.ProjectInfo; +import com.google.cloud.resourcemanager.Project; +import com.google.cloud.resourcemanager.ProjectInfo; ``` Then add the following code to create a project (be sure to change `projectId` to your own unique @@ -163,17 +163,58 @@ while (projectIterator.hasNext()) { } ``` +#### Managing IAM Policies +You can edit [Google Cloud IAM](https://cloud.google.com/iam/) (Identity and Access Management) +policies on the project-level using this library as well. We recommend using the read-modify-write +pattern to make policy changes. This entails reading the project's current policy, updating it +locally, and then sending the modified policy for writing, as shown in the snippet below. First, +add these imports: + +```java +import com.google.cloud.Identity; +import com.google.cloud.resourcemanager.Policy; +import com.google.cloud.resourcemanager.Policy.Role; +``` + +Assuming you have completed the steps above to create the `ResourceManager` service object and load +a project from the server, you just need to add the following code: + +```java +// Get the project's policy +Policy policy = project.getPolicy(); + +// Add a viewer +Policy.Builder modifiedPolicy = policy.toBuilder(); +Identity newViewer = Identity.user(""); +if (policy.bindings().containsKey(Role.viewer())) { + modifiedPolicy.addIdentity(Role.viewer(), newViewer); +} else { + modifiedPolicy.addBinding(Role.viewer(), newViewer); +} + +// Write policy +Policy updatedPolicy = project.replacePolicy(modifiedPolicy.build()); +``` + +Note that the policy you pass in to `replacePolicy` overwrites the original policy. For example, if +the original policy has two bindings and you call `replacePolicy` with a new policy containing only +one binding, the two original bindings are lost. + #### Complete source code -We put together all the code shown above into two programs. Both programs assume that you are +We put together all the code shown above into three programs. The programs assume that you are running from your own desktop and used the Google Cloud SDK to authenticate yourself. The first program creates a project if it does not exist. Complete source code can be found at -[GetOrCreateProject.java](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/GetOrCreateProject.java). +[GetOrCreateProject.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/GetOrCreateProject.java). The second program updates a project if it exists and lists all projects the user has permission to view. Complete source code can be found at -[UpdateAndListProjects.java](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/resourcemanager/snippets/UpdateAndListProjects.java). +[UpdateAndListProjects.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/UpdateAndListProjects.java). + +The third program modifies the IAM policy associated with a project using the read-modify-write +pattern. Complete source code can be found at +[ModifyPolicy.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/resourcemanager/snippets/ModifyPolicy.java) Java Versions ------------- @@ -214,5 +255,5 @@ Apache 2.0 - See [LICENSE] for more information. [TESTING]: https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/TESTING.md#testing-code-that-uses-resource-manager [cloud-platform]: https://cloud.google.com/ [cloud-resourcemanager]: https://cloud.google.com/resource-manager/docs -[resourcemanager-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/resourcemanager/package-summary.html +[resourcemanager-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/resourcemanager/package-summary.html diff --git a/gcloud-java-resourcemanager/pom.xml b/gcloud-java-resourcemanager/pom.xml index c10691d3b07d..272c931326d6 100644 --- a/gcloud-java-resourcemanager/pom.xml +++ b/gcloud-java-resourcemanager/pom.xml @@ -4,13 +4,14 @@ gcloud-java-resourcemanager jar GCloud Java resource manager + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-resourcemanager Java idiomatic client for Google Cloud Resource Manager. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-resourcemanager @@ -33,6 +34,13 @@ + + ${project.groupId} + gcloud-java-core + ${project.version} + test-jar + test + junit junit diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Option.java similarity index 91% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Option.java index 72d62d7fc224..ae53ad7004b4 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Option.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; import com.google.common.base.MoreObjects; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc; import java.io.Serializable; import java.util.Objects; @@ -27,7 +27,7 @@ /** * Base class for Resource Manager operation options. */ -class Option implements Serializable { +abstract class Option implements Serializable { private static final long serialVersionUID = 2655177550880762967L; diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Policy.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Policy.java similarity index 76% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Policy.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Policy.java index 0d7118dcbbd7..f22b13e96268 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Policy.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Policy.java @@ -14,15 +14,14 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; +import com.google.cloud.IamPolicy; +import com.google.cloud.Identity; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.CaseFormat; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import com.google.gcloud.IamPolicy; -import com.google.gcloud.Identity; import java.util.ArrayList; import java.util.HashMap; @@ -41,14 +40,20 @@ * * @see Policy */ -public class Policy extends IamPolicy { +public final class Policy extends IamPolicy { private static final long serialVersionUID = -5573557282693961850L; /** - * Represents legacy roles in an IAM Policy. + * The project-level roles in an IAM policy. This enum is not an exhaustive list of all roles + * you can use in an IAM policy. You can also use service-specific roles (e.g. + * "roles/pubsub.editor"). See the Supported Cloud Platform Services page for links + * to service-specific roles. + * + * @see Supported Cloud + * Platform Services */ - public enum Role { + public enum ProjectRole { /** * Permissions for read-only actions that preserve state. @@ -69,31 +74,29 @@ public enum Role { */ OWNER("roles/owner"); - private String strValue; + private final String value; - private Role(String strValue) { - this.strValue = strValue; + private ProjectRole(String value) { + this.value = value; } - String strValue() { - return strValue; - } - - static Role fromStr(String roleStr) { - return Role.valueOf(CaseFormat.LOWER_CAMEL.to( - CaseFormat.UPPER_UNDERSCORE, roleStr.substring("roles/".length()))); + /** + * Returns the string value associated with the role. + */ + public String value() { + return value; } } /** * Builder for an IAM Policy. */ - public static class Builder extends IamPolicy.Builder { + public static class Builder extends IamPolicy.Builder { private Builder() {} @VisibleForTesting - Builder(Map> bindings, String etag, Integer version) { + Builder(Map> bindings, String etag, Integer version) { bindings(bindings).etag(etag).version(version); } @@ -116,15 +119,20 @@ public Builder toBuilder() { return new Builder(bindings(), etag(), version()); } + @Override + public String toString() { + return toPb().toString(); + } + com.google.api.services.cloudresourcemanager.model.Policy toPb() { com.google.api.services.cloudresourcemanager.model.Policy policyPb = new com.google.api.services.cloudresourcemanager.model.Policy(); List bindingPbList = new LinkedList<>(); - for (Map.Entry> binding : bindings().entrySet()) { + for (Map.Entry> binding : bindings().entrySet()) { com.google.api.services.cloudresourcemanager.model.Binding bindingPb = new com.google.api.services.cloudresourcemanager.model.Binding(); - bindingPb.setRole(binding.getKey().strValue()); + bindingPb.setRole(binding.getKey()); bindingPb.setMembers( Lists.transform( new ArrayList<>(binding.getValue()), @@ -144,11 +152,11 @@ public String apply(Identity identity) { static Policy fromPb( com.google.api.services.cloudresourcemanager.model.Policy policyPb) { - Map> bindings = new HashMap<>(); + Map> bindings = new HashMap<>(); for (com.google.api.services.cloudresourcemanager.model.Binding bindingPb : policyPb.getBindings()) { bindings.put( - Role.fromStr(bindingPb.getRole()), + bindingPb.getRole(), ImmutableSet.copyOf( Lists.transform( bindingPb.getMembers(), diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Project.java similarity index 66% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Project.java index 4d12a31274c0..d728ec3ea4df 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/Project.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.io.ObjectInputStream; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -157,10 +158,10 @@ public Project reload() { * completes, the project is not retrievable by the {@link ResourceManager#get} and * {@link ResourceManager#list} methods. The caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager delete * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager delete */ public void delete() { resourceManager.delete(projectId()); @@ -174,10 +175,10 @@ public void delete() { * state of {@link ProjectInfo.State#DELETE_IN_PROGRESS}, the project cannot be restored. The * caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager undelete * @throws ResourceManagerException upon failure (including when the project can't be restored) + * @see Cloud + * Resource Manager undelete */ public void undelete() { resourceManager.undelete(projectId()); @@ -188,29 +189,88 @@ public void undelete() { * *

    The caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager update * @return the Project representing the new project metadata * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager update */ public Project replace() { return resourceManager.replace(this); } + /** + * Returns the IAM access control policy for this project. Returns {@code null} if the resource + * does not exist or if you do not have adequate permission to view the project or get the policy. + * + * @return the IAM policy for the project + * @throws ResourceManagerException upon failure + * @see + * Resource Manager getIamPolicy + */ + public Policy getPolicy() { + return resourceManager.getPolicy(projectId()); + } + + /** + * Sets the IAM access control policy for this project. Replaces any existing policy. It is + * recommended that you use the read-modify-write pattern. See code samples and important details + * of replacing policies in the documentation for {@link ResourceManager#replacePolicy}. + * + * @return the newly set IAM policy for this project + * @throws ResourceManagerException upon failure + * @see + * Resource Manager setIamPolicy + */ + public Policy replacePolicy(Policy newPolicy) { + return resourceManager.replacePolicy(projectId(), newPolicy); + } + + /** + * Returns the permissions that a caller has on this project. You typically don't call this method + * if you're using Google Cloud Platform directly to manage permissions. This method is intended + * for integration with your proprietary software, such as a customized graphical user interface. + * For example, the Cloud Platform Console tests IAM permissions internally to determine which UI + * should be available to the logged-in user. Each service that supports IAM lists the possible + * permissions; see the Supported Cloud Platform services page below for links to these + * lists. + * + * @return a list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws ResourceManagerException upon failure + * @see + * Resource Manager testIamPermissions + * @see Supported Cloud Platform + * Services + */ + List testPermissions(List permissions) { + return resourceManager.testPermissions(projectId(), permissions); + } + @Override public Builder toBuilder() { return new Builder(this); } @Override - public boolean equals(Object obj) { - return obj instanceof Project && Objects.equals(toPb(), ((Project) obj).toPb()) - && Objects.equals(options, ((Project) obj).options); + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Project.class)) { + return false; + } + Project other = (Project) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), options); } diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ProjectInfo.java similarity index 98% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ProjectInfo.java index 260e8a8e2f26..762ad3db050f 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ProjectInfo.java @@ -12,7 +12,7 @@ * the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; @@ -336,7 +336,9 @@ public Long createTimeMillis() { @Override public boolean equals(Object obj) { - return obj != null && obj.getClass().equals(ProjectInfo.class) + return obj == this + || obj != null + && obj.getClass().equals(ProjectInfo.class) && Objects.equals(toPb(), ((ProjectInfo) obj).toPb()); } diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManager.java similarity index 59% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManager.java index a463937f875c..38925278a484 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManager.java @@ -14,15 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; -import com.google.common.base.Joiner; -import com.google.common.collect.Sets; -import com.google.gcloud.Page; -import com.google.gcloud.Service; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.cloud.FieldSelector; +import com.google.cloud.FieldSelector.Helper; +import com.google.cloud.IamPolicy; +import com.google.cloud.Page; +import com.google.cloud.Service; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.common.collect.ImmutableList; -import java.util.Set; +import java.util.List; /** * An interface for Google Cloud Resource Manager. @@ -40,7 +42,7 @@ public interface ResourceManager extends Service { * {@link ResourceManager#get} or {@link ResourceManager#list}. Project ID is always returned, * even if not specified. */ - enum ProjectField { + enum ProjectField implements FieldSelector { PROJECT_ID("projectId"), NAME("name"), LABELS("labels"), @@ -48,24 +50,18 @@ enum ProjectField { STATE("lifecycleState"), CREATE_TIME("createTime"); + static final List REQUIRED_FIELDS = ImmutableList.of(PROJECT_ID); + private final String selector; ProjectField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(ProjectField... fields) { - Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); - fieldStrings.add(PROJECT_ID.selector()); - for (ProjectField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } /** @@ -88,7 +84,8 @@ private ProjectGetOption(ResourceManagerRpc.Option option, Object value) { * that can be used. */ public static ProjectGetOption fields(ProjectField... fields) { - return new ProjectGetOption(ResourceManagerRpc.Option.FIELDS, ProjectField.selector(fields)); + return new ProjectGetOption(ResourceManagerRpc.Option.FIELDS, + Helper.selector(ProjectField.REQUIRED_FIELDS, fields)); } } @@ -161,9 +158,8 @@ public static ProjectListOption pageSize(int pageSize) { * that can be used. */ public static ProjectListOption fields(ProjectField... fields) { - StringBuilder builder = new StringBuilder(); - builder.append("projects(").append(ProjectField.selector(fields)).append("),nextPageToken"); - return new ProjectListOption(ResourceManagerRpc.Option.FIELDS, builder.toString()); + return new ProjectListOption(ResourceManagerRpc.Option.FIELDS, + Helper.listSelector("projects", ProjectField.REQUIRED_FIELDS, fields)); } } @@ -174,13 +170,13 @@ public static ProjectListOption fields(ProjectField... fields) { * grant permission to others to read or update the project. Several APIs are activated * automatically for the project, including Google Cloud Storage. * - * @see Cloud - * Resource Manager create * @return Project object representing the new project's metadata. The returned object will * include the following read-only fields supplied by the server: project number, lifecycle * state, and creation time. * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager create */ Project create(ProjectInfo project); @@ -201,10 +197,10 @@ public static ProjectListOption fields(ProjectField... fields) { * completes, the project is not retrievable by the {@link ResourceManager#get} and * {@link ResourceManager#list} methods. The caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager delete * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager delete */ void delete(String projectId); @@ -214,10 +210,9 @@ public static ProjectListOption fields(ProjectField... fields) { *

    Returns {@code null} if the project is not found or if the user doesn't have read * permissions for the project. * - * @see Cloud - * Resource Manager get * @throws ResourceManagerException upon failure + * @see + * Cloud Resource Manager get */ Project get(String projectId, ProjectGetOption... options); @@ -228,11 +223,11 @@ public static ProjectListOption fields(ProjectField... fields) { * at the end of the list. Use {@link ProjectListOption} to filter this list, set page size, and * set page tokens. * - * @see Cloud - * Resource Manager list * @return {@code Page}, a page of projects * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager list */ Page list(ProjectListOption... options); @@ -241,11 +236,11 @@ public static ProjectListOption fields(ProjectField... fields) { * *

    The caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager update * @return the Project representing the new project metadata * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager update */ Project replace(ProjectInfo newProject); @@ -257,10 +252,87 @@ public static ProjectListOption fields(ProjectField... fields) { * state of {@link ProjectInfo.State#DELETE_IN_PROGRESS}, the project cannot be restored. The * caller must have modify permissions for this project. * - * @see Cloud - * Resource Manager undelete * @throws ResourceManagerException upon failure + * @see Cloud + * Resource Manager undelete */ void undelete(String projectId); + + /** + * Returns the IAM access control policy for the specified project. Returns {@code null} if the + * resource does not exist or if you do not have adequate permission to view the project or get + * the policy. + * + * @throws ResourceManagerException upon failure + * @see + * Resource Manager getIamPolicy + */ + Policy getPolicy(String projectId); + + /** + * Sets the IAM access control policy for the specified project. Replaces any existing policy. The + * following constraints apply: + *

      + *
    • Projects currently support only user:{emailid} and serviceAccount:{emailid} + * members in a binding of a policy. + *
    • To be added as an owner, a user must be invited via Cloud Platform console and must accept + * the invitation. + *
    • Members cannot be added to more than one role in the same policy. + *
    • There must be at least one owner who has accepted the Terms of Service (ToS) agreement in + * the policy. An attempt to set a policy that removes the last ToS-accepted owner from the + * policy will fail. + *
    • Calling this method requires enabling the App Engine Admin API. + *
    + * Note: Removing service accounts from policies or changing their roles can render services + * completely inoperable. It is important to understand how the service account is being used + * before removing or updating its roles. + * + *

    It is recommended that you use the read-modify-write pattern. This pattern entails reading + * the project's current policy, updating it locally, and then sending the modified policy for + * writing. Cloud IAM solves the problem of conflicting processes simultaneously attempting to + * modify a policy by using the {@link IamPolicy#etag etag} property. This property is used to + * verify whether the policy has changed since the last request. When you make a request to Cloud + * IAM with an etag value, Cloud IAM compares the etag value in the request with the existing etag + * value associated with the policy. It writes the policy only if the etag values match. If the + * etags don't match, a {@code ResourceManagerException} is thrown, denoting that the server + * aborted update. If an etag is not provided, the policy is overwritten blindly. + * + *

    An example of using the read-write-modify pattern is as follows: + *

     {@code
    +   * Policy currentPolicy = resourceManager.getPolicy("my-project-id");
    +   * Policy modifiedPolicy =
    +   *     current.toBuilder().removeIdentity(Role.VIEWER, Identity.user("user@gmail.com"));
    +   * Policy newPolicy = resourceManager.replacePolicy("my-project-id", modified);
    +   * }
    +   * 
    + * + * @throws ResourceManagerException upon failure + * @see + * Resource Manager setIamPolicy + */ + Policy replacePolicy(String projectId, Policy newPolicy); + + /** + * Returns the permissions that a caller has on the specified project. You typically don't call + * this method if you're using Google Cloud Platform directly to manage permissions. This method + * is intended for integration with your proprietary software, such as a customized graphical user + * interface. For example, the Cloud Platform Console tests IAM permissions internally to + * determine which UI should be available to the logged-in user. Each service that supports IAM + * lists the possible permissions; see the Supported Cloud Platform services page below for + * links to these lists. + * + * @return A list of booleans representing whether the caller has the permissions specified (in + * the order of the given permissions) + * @throws ResourceManagerException upon failure + * @see + * Resource Manager testIamPermissions + * @see Supported Cloud Platform + * Services + */ + List testPermissions(String projectId, List permissions); } diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerException.java similarity index 92% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerException.java index 32a2998791c9..1df014e450ee 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerException.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryHelper.RetryInterruptedException; import java.io.IOException; import java.util.Set; diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerFactory.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerFactory.java similarity index 90% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerFactory.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerFactory.java index 256fc321e4e1..7ef607cac23b 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerFactory.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerFactory.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; -import com.google.gcloud.ServiceFactory; +import com.google.cloud.ServiceFactory; /** * An interface for ResourceManager factories. diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerImpl.java similarity index 68% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerImpl.java index fb699dcb06f0..4054a4b2443a 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerImpl.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerImpl.java @@ -14,24 +14,25 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; +import static com.google.cloud.RetryHelper.runWithRetries; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.gcloud.RetryHelper.runWithRetries; +import com.google.cloud.BaseService; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.PageImpl.NextPageFetcher; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Tuple; 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.Page; -import com.google.gcloud.PageImpl; -import com.google.gcloud.PageImpl.NextPageFetcher; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Tuple; +import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -55,8 +56,8 @@ public com.google.api.services.cloudresourcemanager.model.Project call() { return resourceManagerRpc.create(project.toPb()); } }, options().retryParams(), EXCEPTION_HANDLER)); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } @@ -70,8 +71,8 @@ public Void call() { return null; } }, options().retryParams(), EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } @@ -87,8 +88,8 @@ public com.google.api.services.cloudresourcemanager.model.Project call() { } }, options().retryParams(), EXCEPTION_HANDLER); return answer == null ? null : Project.fromPb(this, answer); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } @@ -146,8 +147,8 @@ public Project apply( }); return new PageImpl<>( new ProjectPageFetcher(serviceOptions, cursor, optionsMap), cursor, projects); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } @@ -161,8 +162,8 @@ public com.google.api.services.cloudresourcemanager.model.Project call() { return resourceManagerRpc.replace(newProject.toPb()); } }, options().retryParams(), EXCEPTION_HANDLER)); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } @@ -176,8 +177,55 @@ public Void call() { return null; } }, options().retryParams(), EXCEPTION_HANDLER); - } catch (RetryHelperException e) { - throw ResourceManagerException.translateAndThrow(e); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); + } + } + + @Override + public Policy getPolicy(final String projectId) { + try { + com.google.api.services.cloudresourcemanager.model.Policy answer = + runWithRetries( + new Callable() { + @Override + public com.google.api.services.cloudresourcemanager.model.Policy call() { + return resourceManagerRpc.getPolicy(projectId); + } + }, options().retryParams(), EXCEPTION_HANDLER); + return answer == null ? null : Policy.fromPb(answer); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); + } + } + + @Override + public Policy replacePolicy(final String projectId, final Policy newPolicy) { + try { + return Policy.fromPb(runWithRetries( + new Callable() { + @Override + public com.google.api.services.cloudresourcemanager.model.Policy call() { + return resourceManagerRpc.replacePolicy(projectId, newPolicy.toPb()); + } + }, options().retryParams(), EXCEPTION_HANDLER)); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); + } + } + + @Override + public List testPermissions(final String projectId, final List permissions) { + try { + return runWithRetries( + new Callable>() { + @Override + public List call() { + return resourceManagerRpc.testPermissions(projectId, permissions); + } + }, options().retryParams(), EXCEPTION_HANDLER); + } catch (RetryHelperException ex) { + throw ResourceManagerException.translateAndThrow(ex); } } diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerOptions.java similarity index 91% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerOptions.java index c744864147c2..d37dfd298aa0 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/ResourceManagerOptions.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; +import com.google.cloud.ServiceOptions; +import com.google.cloud.resourcemanager.spi.DefaultResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpcFactory; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.resourcemanager.spi.DefaultResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpcFactory; import java.util.Set; @@ -112,6 +112,7 @@ public int hashCode() { return baseHashCode(); } + @SuppressWarnings("unchecked") @Override public Builder toBuilder() { return new Builder(this); diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/package-info.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/package-info.java similarity index 90% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/package-info.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/package-info.java index d1794447e9fb..0329b0a89b4a 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/package-info.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/package-info.java @@ -19,7 +19,7 @@ * *

    Here's a simple usage example for using gcloud-java from App/Compute Engine. This example * creates a project if it does not exist. For the complete source code see - * + * * GetOrCreateProject.java. *

     {@code
      * ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service();
    @@ -33,7 +33,7 @@
      * 

    * This second example shows how to update a project if it exists and list all projects the user has * permission to view. For the complete source code see - * + * * UpdateAndListProjects.java. *

     {@code
      * ResourceManager resourceManager = ResourceManagerOptions.defaultInstance().service();
    @@ -58,4 +58,4 @@
      * @see Google Cloud Resource Manager
      */
     
    -package com.google.gcloud.resourcemanager;
    +package com.google.cloud.resourcemanager;
    diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/DefaultResourceManagerRpc.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java
    similarity index 54%
    rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/DefaultResourceManagerRpc.java
    rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java
    index 2ef0d8c65ff2..427478baef5f 100644
    --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/DefaultResourceManagerRpc.java
    +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/DefaultResourceManagerRpc.java
    @@ -1,9 +1,10 @@
    -package com.google.gcloud.resourcemanager.spi;
    +package com.google.cloud.resourcemanager.spi;
     
    -import static com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Option.FIELDS;
    -import static com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Option.FILTER;
    -import static com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Option.PAGE_SIZE;
    -import static com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Option.PAGE_TOKEN;
    +import static com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Option.FIELDS;
    +import static com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Option.FILTER;
    +import static com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Option.PAGE_SIZE;
    +import static com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Option.PAGE_TOKEN;
    +import static com.google.common.base.MoreObjects.firstNonNull;
     import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
     import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
     
    @@ -11,13 +12,22 @@
     import com.google.api.client.http.HttpTransport;
     import com.google.api.client.json.jackson.JacksonFactory;
     import com.google.api.services.cloudresourcemanager.Cloudresourcemanager;
    +import com.google.api.services.cloudresourcemanager.model.GetIamPolicyRequest;
     import com.google.api.services.cloudresourcemanager.model.ListProjectsResponse;
    +import com.google.api.services.cloudresourcemanager.model.Policy;
     import com.google.api.services.cloudresourcemanager.model.Project;
    -import com.google.gcloud.resourcemanager.ResourceManagerException;
    -import com.google.gcloud.resourcemanager.ResourceManagerOptions;
    +import com.google.api.services.cloudresourcemanager.model.SetIamPolicyRequest;
    +import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsRequest;
    +import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsResponse;
    +import com.google.cloud.resourcemanager.ResourceManagerException;
    +import com.google.cloud.resourcemanager.ResourceManagerOptions;
    +import com.google.common.collect.ImmutableList;
    +import com.google.common.collect.ImmutableSet;
     
     import java.io.IOException;
    +import java.util.List;
     import java.util.Map;
    +import java.util.Set;
     
     public class DefaultResourceManagerRpc implements ResourceManagerRpc {
     
    @@ -107,5 +117,51 @@ public Project replace(Project project) {
           throw translate(ex);
         }
       }
    -}
     
    +  @Override
    +  public Policy getPolicy(String projectId) throws ResourceManagerException {
    +    try {
    +      return resourceManager.projects()
    +          .getIamPolicy(projectId, new GetIamPolicyRequest())
    +          .execute();
    +    } catch (IOException ex) {
    +      ResourceManagerException translated = translate(ex);
    +      if (translated.code() == HTTP_FORBIDDEN) {
    +        // Service returns permission denied if policy doesn't exist.
    +        return null;
    +      } else {
    +        throw translated;
    +      }
    +    }
    +  }
    +
    +  @Override
    +  public Policy replacePolicy(String projectId, Policy newPolicy) throws ResourceManagerException {
    +    try {
    +      return resourceManager.projects()
    +          .setIamPolicy(projectId, new SetIamPolicyRequest().setPolicy(newPolicy)).execute();
    +    } catch (IOException ex) {
    +      throw translate(ex);
    +    }
    +  }
    +
    +  @Override
    +  public List testPermissions(String projectId, List permissions)
    +      throws ResourceManagerException {
    +    try {
    +      TestIamPermissionsResponse response = resourceManager.projects()
    +          .testIamPermissions(
    +              projectId, new TestIamPermissionsRequest().setPermissions(permissions))
    +          .execute();
    +      Set permissionsOwned =
    +          ImmutableSet.copyOf(firstNonNull(response.getPermissions(), ImmutableList.of()));
    +      ImmutableList.Builder answer = ImmutableList.builder();
    +      for (String p : permissions) {
    +        answer.add(permissionsOwned.contains(p));
    +      }
    +      return answer.build();
    +    } catch (IOException ex) {
    +      throw translate(ex);
    +    }
    +  }
    +}
    diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpc.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpc.java
    similarity index 77%
    rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpc.java
    rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpc.java
    index 54531edd5ed5..11e028be93a3 100644
    --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpc.java
    +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpc.java
    @@ -14,11 +14,13 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.resourcemanager.spi;
    +package com.google.cloud.resourcemanager.spi;
     
    +import com.google.api.services.cloudresourcemanager.model.Policy;
     import com.google.api.services.cloudresourcemanager.model.Project;
    -import com.google.gcloud.resourcemanager.ResourceManagerException;
    +import com.google.cloud.resourcemanager.ResourceManagerException;
     
    +import java.util.List;
     import java.util.Map;
     
     public interface ResourceManagerRpc {
    @@ -121,5 +123,27 @@ public Y y() {
        */
       Project replace(Project project);
     
    +  /**
    +   * Returns the IAM policy associated with a project.
    +   *
    +   * @throws ResourceManagerException upon failure
    +   */
    +  Policy getPolicy(String projectId);
    +
    +  /**
    +   * Replaces the IAM policy associated with the given project.
    +   *
    +   * @throws ResourceManagerException upon failure
    +   */
    +  Policy replacePolicy(String projectId, Policy newPolicy);
    +
    +  /**
    +   * Tests whether the caller has the given permissions. Returns a list of booleans corresponding to
    +   * whether or not the user has the permission in the same position of input list.
    +   *
    +   * @throws ResourceManagerException upon failure
    +   */
    +  List testPermissions(String projectId, List permissions);
    +
       // TODO(ajaykannan): implement "Organization" functionality when available (issue #319)
     }
    diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpcFactory.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpcFactory.java
    similarity index 84%
    rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpcFactory.java
    rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpcFactory.java
    index 4dbd1a00d4c7..ef6a96ac4b5e 100644
    --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/spi/ResourceManagerRpcFactory.java
    +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/spi/ResourceManagerRpcFactory.java
    @@ -14,10 +14,10 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.resourcemanager.spi;
    +package com.google.cloud.resourcemanager.spi;
     
    -import com.google.gcloud.resourcemanager.ResourceManagerOptions;
    -import com.google.gcloud.spi.ServiceRpcFactory;
    +import com.google.cloud.resourcemanager.ResourceManagerOptions;
    +import com.google.cloud.spi.ServiceRpcFactory;
     
     /**
      * An interface for Resource Manager RPC factory.
    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/cloud/resourcemanager/testing/LocalResourceManagerHelper.java
    similarity index 80%
    rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java
    rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java
    index cda2dd1e00ea..6c16372c0424 100644
    --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/LocalResourceManagerHelper.java
    +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelper.java
    @@ -1,19 +1,24 @@
    -package com.google.gcloud.resourcemanager.testing;
    +package com.google.cloud.resourcemanager.testing;
     
     import static com.google.common.base.Preconditions.checkArgument;
     import static com.google.common.base.Preconditions.checkNotNull;
     import static java.net.HttpURLConnection.HTTP_OK;
     
     import com.google.api.client.json.JsonFactory;
    +import com.google.api.services.cloudresourcemanager.model.Binding;
    +import com.google.api.services.cloudresourcemanager.model.Policy;
     import com.google.api.services.cloudresourcemanager.model.Project;
    +import com.google.api.services.cloudresourcemanager.model.SetIamPolicyRequest;
    +import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsRequest;
    +import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsResponse;
    +import com.google.cloud.AuthCredentials;
    +import com.google.cloud.resourcemanager.ResourceManagerOptions;
     import com.google.common.base.Joiner;
     import com.google.common.base.Objects;
     import com.google.common.collect.ImmutableList;
     import com.google.common.collect.ImmutableMap;
     import com.google.common.collect.ImmutableSet;
     import com.google.common.io.ByteStreams;
    -import com.google.gcloud.AuthCredentials;
    -import com.google.gcloud.resourcemanager.ResourceManagerOptions;
     
     import com.sun.net.httpserver.Headers;
     import com.sun.net.httpserver.HttpExchange;
    @@ -30,11 +35,13 @@
     import java.net.URISyntaxException;
     import java.nio.charset.StandardCharsets;
     import java.util.ArrayList;
    +import java.util.Collections;
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
     import java.util.Random;
     import java.util.Set;
    +import java.util.UUID;
     import java.util.concurrent.ConcurrentSkipListMap;
     import java.util.logging.Level;
     import java.util.logging.Logger;
    @@ -46,7 +53,26 @@
      * Utility to create a local Resource Manager mock for testing.
      *
      * 

    The mock runs in a separate thread, listening for HTTP requests on the local machine at an - * ephemeral port. + * ephemeral port. While this mock attempts to simulate the Cloud Resource Manager, there are some + * divergences in behavior. The following is a non-exhaustive list of some of those behavioral + * differences: + * + *

      + *
    • This mock assumes you have adequate permissions for any action. Related to this, + * testIamPermissions always indicates that the caller has all permissions listed in the + * request. + *
    • IAM policies are set to an empty policy with version 0 (only legacy roles supported) upon + * project creation. The actual service will not have an empty list of bindings and may also + * set your version to 1. + *
    • There is no input validation for the policy provided when replacing a policy or calling + * testIamPermissions. + *
    • In this mock, projects never move from the DELETE_REQUESTED lifecycle state to + * DELETE_IN_PROGRESS without an explicit call to the utility method + * {@link #changeLifecycleState}. Similarly, a project is never completely removed without an + * explicit call to the utility method {@link #removeProject}. + *
    • The messages in the error responses given by this mock do not necessarily match the messages + * given by the actual service. + *
    */ @SuppressWarnings("restriction") public class LocalResourceManagerHelper { @@ -78,6 +104,7 @@ public class LocalResourceManagerHelper { private final HttpServer server; private final ConcurrentSkipListMap projects = new ConcurrentSkipListMap<>(); + private final Map policies = new HashMap<>(); private final int port; private static class Response { @@ -99,6 +126,7 @@ String body() { } private enum Error { + ABORTED(409, "global", "aborted", "ABORTED"), ALREADY_EXISTS(409, "global", "alreadyExists", "ALREADY_EXISTS"), PERMISSION_DENIED(403, "global", "forbidden", "PERMISSION_DENIED"), FAILED_PRECONDITION(400, "global", "failedPrecondition", "FAILED_PRECONDITION"), @@ -150,13 +178,7 @@ public void handle(HttpExchange exchange) { try { switch (requestMethod) { case "POST": - if (path.endsWith(":undelete")) { - response = undelete(projectIdFromUri(path)); - } else { - String requestBody = - decodeContent(exchange.getRequestHeaders(), exchange.getRequestBody()); - response = create(jsonFactory.fromString(requestBody, Project.class)); - } + response = handlePost(exchange, path); break; case "DELETE": response = delete(projectIdFromUri(path)); @@ -187,6 +209,30 @@ public void handle(HttpExchange exchange) { } } + private Response handlePost(HttpExchange exchange, String path) throws IOException { + String requestBody = decodeContent(exchange.getRequestHeaders(), exchange.getRequestBody()); + if (!path.contains(":")) { + return create(jsonFactory.fromString(requestBody, Project.class)); + } else { + switch (path.split(":", 2)[1]) { + case "undelete": + return undelete(projectIdFromUri(path)); + case "getIamPolicy": + return getPolicy(projectIdFromUri(path)); + case "setIamPolicy": + return replacePolicy(projectIdFromUri(path), + jsonFactory.fromString(requestBody, SetIamPolicyRequest.class).getPolicy()); + case "testIamPermissions": + return testPermissions(projectIdFromUri(path), + jsonFactory.fromString(requestBody, TestIamPermissionsRequest.class) + .getPermissions()); + default: + return Error.BAD_REQUEST.response( + "The server could not understand the following request URI: POST " + path); + } + } + } + private static void writeResponse(HttpExchange exchange, Response response) { exchange.getResponseHeaders().set("Content-type", "application/json; charset=UTF-8"); OutputStream outputStream = exchange.getResponseBody(); @@ -259,7 +305,7 @@ private static Map parseListOptions(String query) throws IOExcep options.put("pageToken", argEntry[1]); break; case "pageSize": - int pageSize = Integer.valueOf(argEntry[1]); + int pageSize = Integer.parseInt(argEntry[1]); if (pageSize < 1) { throw new IOException("Page size must be greater than 0."); } @@ -317,7 +363,7 @@ private static boolean isValidIdOrLabel(String value, int minLength, int maxLeng return value.length() >= minLength && value.length() <= maxLength; } - Response create(Project project) { + synchronized Response create(Project project) { String customErrorMessage = checkForProjectErrors(project); if (customErrorMessage != null) { return Error.INVALID_ARGUMENT.response(customErrorMessage); @@ -329,6 +375,11 @@ Response create(Project project) { return Error.ALREADY_EXISTS.response( "A project with the same project ID (" + project.getProjectId() + ") already exists."); } + Policy emptyPolicy = new Policy() + .setBindings(Collections.emptyList()) + .setEtag(UUID.randomUUID().toString()) + .setVersion(0); + policies.put(project.getProjectId(), emptyPolicy); try { String createdProjectStr = jsonFactory.toString(project); return new Response(HTTP_OK, createdProjectStr); @@ -540,6 +591,53 @@ synchronized Response undelete(String projectId) { return response; } + synchronized Response getPolicy(String projectId) { + Policy policy = policies.get(projectId); + if (policy == null) { + return Error.PERMISSION_DENIED.response("Project " + projectId + " not found."); + } + try { + return new Response(HTTP_OK, jsonFactory.toString(policy)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response( + "Error when serializing the IAM policy for " + projectId); + } + } + + synchronized Response replacePolicy(String projectId, Policy policy) { + Policy originalPolicy = policies.get(projectId); + if (originalPolicy == null) { + return Error.PERMISSION_DENIED.response("Error when replacing the policy for " + projectId + + " because the project was not found."); + } + String etag = policy.getEtag(); + if (etag != null && !originalPolicy.getEtag().equals(etag)) { + return Error.ABORTED.response("Policy etag mismatch when replacing the policy for project " + + projectId + ", please retry the read."); + } + policy.setEtag(UUID.randomUUID().toString()); + policy.setVersion(originalPolicy.getVersion()); + policies.put(projectId, policy); + try { + return new Response(HTTP_OK, jsonFactory.toString(policy)); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response( + "Error when serializing the policy for project " + projectId); + } + } + + synchronized Response testPermissions(String projectId, List permissions) { + if (!projects.containsKey(projectId)) { + return Error.PERMISSION_DENIED.response("Project " + projectId + " not found."); + } + try { + return new Response(HTTP_OK, + jsonFactory.toString(new TestIamPermissionsResponse().setPermissions(permissions))); + } catch (IOException e) { + return Error.INTERNAL_ERROR.response("Error when serializing permissions " + permissions); + } + } + private LocalResourceManagerHelper() { try { server = HttpServer.create(new InetSocketAddress(0), 0); @@ -611,6 +709,7 @@ public synchronized boolean changeLifecycleState(String projectId, String lifecy public synchronized boolean removeProject(String projectId) { // Because this method is synchronized, any code that relies on non-atomic read/write operations // should not fail if that code is also synchronized. - return projects.remove(checkNotNull(projectId)) != null; + policies.remove(checkNotNull(projectId)); + return projects.remove(projectId) != null; } } diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/package-info.java b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/package-info.java similarity index 90% rename from gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/package-info.java rename to gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/package-info.java index 7e5519f7d085..d007d00eaeab 100644 --- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/testing/package-info.java +++ b/gcloud-java-resourcemanager/src/main/java/com/google/cloud/resourcemanager/testing/package-info.java @@ -21,12 +21,13 @@ * Before the test: *
     {@code
      * LocalResourceManagerHelper resourceManagerHelper = LocalResourceManagerHelper.create();
    + * resourceManagerHelper.start();
      * ResourceManager resourceManager = resourceManagerHelper.options().service();
    - * } 
    + * }
    * *

    After the test: *

     {@code
      * resourceManagerHelper.stop();
    - * } 
    + * }
    */ -package com.google.gcloud.resourcemanager.testing; +package com.google.cloud.resourcemanager.testing; diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/OptionTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/OptionTest.java new file mode 100644 index 000000000000..097a5c61c13d --- /dev/null +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/OptionTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 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.cloud.resourcemanager; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class OptionTest { + + private static final ResourceManagerRpc.Option RPC_OPTION = ResourceManagerRpc.Option.FILTER; + private static final ResourceManagerRpc.Option ANOTHER_RPC_OPTION = + ResourceManagerRpc.Option.FIELDS; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final Option OPTION = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_EQUALS = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_NOT_EQUALS1 = new Option(RPC_OPTION, OTHER_VALUE) {}; + private static final Option OPTION_NOT_EQUALS2 = new Option(ANOTHER_RPC_OPTION, VALUE) {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(RPC_OPTION, OPTION.rpcOption()); + assertEquals(VALUE, OPTION.value()); + Option option = new Option(RPC_OPTION, null) {}; + assertEquals(RPC_OPTION, option.rpcOption()); + assertNull(option.value()); + thrown.expect(NullPointerException.class); + new Option(null, VALUE) {}; + } +} diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/PolicyTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/PolicyTest.java similarity index 67% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/PolicyTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/PolicyTest.java index 05d1b85bdbed..a99286b119ba 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/PolicyTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/PolicyTest.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; -import com.google.common.collect.ImmutableSet; -import com.google.gcloud.Identity; +import com.google.cloud.Identity; +import com.google.cloud.resourcemanager.Policy.ProjectRole; import org.junit.Test; @@ -33,8 +34,10 @@ public class PolicyTest { private static final Identity GROUP = Identity.group("group@gmail.com"); private static final Identity DOMAIN = Identity.domain("google.com"); private static final Policy SIMPLE_POLICY = Policy.builder() - .addBinding(Policy.Role.VIEWER, ImmutableSet.of(USER, SERVICE_ACCOUNT, ALL_USERS)) - .addBinding(Policy.Role.EDITOR, ImmutableSet.of(ALL_AUTH_USERS, GROUP, DOMAIN)) + .addIdentity(ProjectRole.OWNER.value(), USER) + .addIdentity(ProjectRole.VIEWER.value(), ALL_USERS) + .addIdentity(ProjectRole.EDITOR.value(), ALL_AUTH_USERS, DOMAIN) + .addIdentity("roles/some-role", SERVICE_ACCOUNT, GROUP) .build(); private static final Policy FULL_POLICY = new Policy.Builder(SIMPLE_POLICY.bindings(), "etag", 1).build(); @@ -50,4 +53,16 @@ public void testPolicyToAndFromPb() { assertEquals(FULL_POLICY, Policy.fromPb(FULL_POLICY.toPb())); assertEquals(SIMPLE_POLICY, Policy.fromPb(SIMPLE_POLICY.toPb())); } + + @Test + public void testEquals() { + Policy copy = Policy.builder() + .addIdentity(ProjectRole.OWNER.value(), USER) + .addIdentity(ProjectRole.VIEWER.value(), ALL_USERS) + .addIdentity(ProjectRole.EDITOR.value(), ALL_AUTH_USERS, DOMAIN) + .addIdentity("roles/some-role", SERVICE_ACCOUNT, GROUP) + .build(); + assertEquals(SIMPLE_POLICY, copy); + assertNotEquals(SIMPLE_POLICY, FULL_POLICY); + } } diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectInfoTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectInfoTest.java similarity index 99% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectInfoTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectInfoTest.java index 3aaef8047322..2b276f485a72 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectInfoTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectInfoTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectTest.java similarity index 72% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectTest.java index 4e239acc45ef..a40336ae665e 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ProjectTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ProjectTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; @@ -25,12 +25,17 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import com.google.cloud.Identity; +import com.google.cloud.resourcemanager.Policy.ProjectRole; +import com.google.cloud.resourcemanager.ProjectInfo.ResourceId; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.List; import java.util.Map; public class ProjectTest { @@ -47,6 +52,13 @@ public class ProjectTest { .createTimeMillis(CREATE_TIME_MILLIS) .state(STATE) .build(); + private static final Identity USER = Identity.user("abc@gmail.com"); + private static final Identity SERVICE_ACCOUNT = + Identity.serviceAccount("service-account@gmail.com"); + private static final Policy POLICY = Policy.builder() + .addIdentity(ProjectRole.OWNER.value(), USER) + .addIdentity(ProjectRole.EDITOR.value(), SERVICE_ACCOUNT) + .build(); private ResourceManager serviceMockReturnsOptions = createStrictMock(ResourceManager.class); private ResourceManagerOptions mockOptions = createMock(ResourceManagerOptions.class); @@ -84,12 +96,12 @@ public void testToBuilder() { @Test public void testBuilder() { - initializeExpectedProject(4); - expect(resourceManager.options()).andReturn(mockOptions).times(4); + expect(resourceManager.options()).andReturn(mockOptions).times(7); replay(resourceManager); Project.Builder builder = - new Project.Builder(new Project(resourceManager, new ProjectInfo.BuilderImpl(PROJECT_ID))); - Project project = builder.name(NAME) + new Project.Builder(new Project(resourceManager, new ProjectInfo.BuilderImpl("wrong-id"))); + Project project = builder.projectId(PROJECT_ID) + .name(NAME) .labels(LABELS) .projectNumber(PROJECT_NUMBER) .createTimeMillis(CREATE_TIME_MILLIS) @@ -102,6 +114,23 @@ public void testBuilder() { assertEquals(CREATE_TIME_MILLIS, project.createTimeMillis()); assertEquals(STATE, project.state()); assertEquals(resourceManager.options(), project.resourceManager().options()); + assertNull(project.parent()); + ResourceId parent = new ResourceId("id", "type"); + project = project.toBuilder() + .clearLabels() + .addLabel("k3", "v3") + .addLabel("k4", "v4") + .removeLabel("k4") + .parent(parent) + .build(); + assertEquals(PROJECT_ID, project.projectId()); + assertEquals(NAME, project.name()); + assertEquals(ImmutableMap.of("k3", "v3"), project.labels()); + assertEquals(PROJECT_NUMBER, project.projectNumber()); + assertEquals(CREATE_TIME_MILLIS, project.createTimeMillis()); + assertEquals(STATE, project.state()); + assertEquals(resourceManager.options(), project.resourceManager().options()); + assertEquals(parent, project.parent()); } @Test @@ -187,6 +216,39 @@ public void testReplace() { compareProjectInfos(expectedReplacedProject, actualReplacedProject); } + @Test + public void testGetPolicy() { + expect(resourceManager.options()).andReturn(mockOptions).times(1); + expect(resourceManager.getPolicy(PROJECT_ID)).andReturn(POLICY); + replay(resourceManager); + initializeProject(); + assertEquals(POLICY, project.getPolicy()); + } + + @Test + public void testReplacePolicy() { + expect(resourceManager.options()).andReturn(mockOptions).times(1); + expect(resourceManager.replacePolicy(PROJECT_ID, POLICY)).andReturn(POLICY); + replay(resourceManager); + initializeProject(); + assertEquals(POLICY, project.replacePolicy(POLICY)); + } + + @Test + public void testTestPermissions() { + List response = ImmutableList.of(true, true); + String getPermission = "resourcemanager.projects.get"; + String deletePermission = "resourcemanager.projects.delete"; + expect(resourceManager.options()).andReturn(mockOptions).times(1); + expect(resourceManager.testPermissions( + PROJECT_ID, ImmutableList.of(getPermission, deletePermission))) + .andReturn(response); + replay(resourceManager); + initializeProject(); + assertEquals( + response, project.testPermissions(ImmutableList.of(getPermission, deletePermission))); + } + private void compareProjects(Project expected, Project value) { assertEquals(expected, value); compareProjectInfos(expected, value); diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerExceptionTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerExceptionTest.java similarity index 95% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerExceptionTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerExceptionTest.java index 388f38f31c35..2ac8d1acba7f 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerExceptionTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerExceptionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -25,8 +25,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; import org.junit.Test; diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerImplTest.java similarity index 83% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerImplTest.java index 5b172d6a070e..92bf6fd96223 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/ResourceManagerImplTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/ResourceManagerImplTest.java @@ -14,25 +14,29 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.Identity; +import com.google.cloud.Page; +import com.google.cloud.resourcemanager.Policy.ProjectRole; +import com.google.cloud.resourcemanager.ProjectInfo.ResourceId; +import com.google.cloud.resourcemanager.ResourceManager.ProjectField; +import com.google.cloud.resourcemanager.ResourceManager.ProjectGetOption; +import com.google.cloud.resourcemanager.ResourceManager.ProjectListOption; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpcFactory; +import com.google.cloud.resourcemanager.testing.LocalResourceManagerHelper; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.Page; -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.spi.ResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpcFactory; -import com.google.gcloud.resourcemanager.testing.LocalResourceManagerHelper; import org.easymock.EasyMock; import org.junit.AfterClass; @@ -43,6 +47,7 @@ import org.junit.rules.ExpectedException; import java.util.Iterator; +import java.util.List; import java.util.Map; public class ResourceManagerImplTest { @@ -65,6 +70,12 @@ public class ResourceManagerImplTest { .parent(PARENT) .build(); private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); + private static final Policy POLICY = + Policy.builder() + .addIdentity(ProjectRole.OWNER.value(), Identity.user("me@gmail.com")) + .addIdentity( + ProjectRole.EDITOR.value(), Identity.serviceAccount("serviceaccount@gmail.com")) + .build(); @Rule public ExpectedException thrown = ExpectedException.none(); @@ -320,6 +331,58 @@ public void testUndelete() { } } + @Test + public void testGetPolicy() { + assertNull(RESOURCE_MANAGER.getPolicy(COMPLETE_PROJECT.projectId())); + RESOURCE_MANAGER.create(COMPLETE_PROJECT); + RESOURCE_MANAGER.replacePolicy(COMPLETE_PROJECT.projectId(), POLICY); + Policy retrieved = RESOURCE_MANAGER.getPolicy(COMPLETE_PROJECT.projectId()); + assertEquals(POLICY.bindings(), retrieved.bindings()); + assertNotNull(retrieved.etag()); + assertEquals(0, retrieved.version().intValue()); + } + + @Test + public void testReplacePolicy() { + try { + RESOURCE_MANAGER.replacePolicy("nonexistent-project", POLICY); + fail("Project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().endsWith("project was not found.")); + } + RESOURCE_MANAGER.create(PARTIAL_PROJECT); + Policy oldPolicy = RESOURCE_MANAGER.getPolicy(PARTIAL_PROJECT.projectId()); + RESOURCE_MANAGER.replacePolicy(PARTIAL_PROJECT.projectId(), POLICY); + try { + RESOURCE_MANAGER.replacePolicy(PARTIAL_PROJECT.projectId(), oldPolicy); + fail("Policy with an invalid etag didn't cause error."); + } catch (ResourceManagerException e) { + assertEquals(409, e.code()); + assertTrue(e.getMessage().contains("Policy etag mismatch")); + } + String originalEtag = RESOURCE_MANAGER.getPolicy(PARTIAL_PROJECT.projectId()).etag(); + Policy newPolicy = RESOURCE_MANAGER.replacePolicy(PARTIAL_PROJECT.projectId(), POLICY); + assertEquals(POLICY.bindings(), newPolicy.bindings()); + assertNotNull(newPolicy.etag()); + assertNotEquals(originalEtag, newPolicy.etag()); + } + + @Test + public void testTestPermissions() { + List permissions = ImmutableList.of("resourcemanager.projects.get"); + try { + RESOURCE_MANAGER.testPermissions("nonexistent-project", permissions); + fail("Nonexistent project"); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertEquals("Project nonexistent-project not found.", e.getMessage()); + } + RESOURCE_MANAGER.create(PARTIAL_PROJECT); + assertEquals(ImmutableList.of(true), + RESOURCE_MANAGER.testPermissions(PARTIAL_PROJECT.projectId(), permissions)); + } + @Test public void testRetryableException() { ResourceManagerRpcFactory rpcFactoryMock = EasyMock.createMock(ResourceManagerRpcFactory.class); diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/SerializationTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/SerializationTest.java similarity index 52% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/SerializationTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/SerializationTest.java index 35b72ae1713f..52d8c9692a88 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/SerializationTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/SerializationTest.java @@ -14,28 +14,19 @@ * limitations under the License. */ -package com.google.gcloud.resourcemanager; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; +package com.google.cloud.resourcemanager; +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.Identity; +import com.google.cloud.PageImpl; +import com.google.cloud.Restorable; +import com.google.cloud.resourcemanager.Policy.ProjectRole; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.gcloud.Identity; -import com.google.gcloud.PageImpl; -import com.google.gcloud.RetryParams; - -import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; -public class SerializationTest { +public class SerializationTest extends BaseSerializationTest { private static final ResourceManager RESOURCE_MANAGER = ResourceManagerOptions.defaultInstance().service(); @@ -56,44 +47,24 @@ public class SerializationTest { private static final ResourceManager.ProjectListOption PROJECT_LIST_OPTION = ResourceManager.ProjectListOption.filter("name:*"); private static final Policy POLICY = Policy.builder() - .addBinding(Policy.Role.VIEWER, ImmutableSet.of(Identity.user("abc@gmail.com"))) + .addIdentity(ProjectRole.VIEWER.value(), Identity.user("abc@gmail.com")) .build(); + private static final ResourceManagerException RESOURCE_MANAGER_EXCEPTION = + new ResourceManagerException(42, "message"); - @Test - public void testServiceOptions() throws Exception { + @Override + protected Serializable[] serializableObjects() { ResourceManagerOptions options = ResourceManagerOptions.builder().build(); - ResourceManagerOptions serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - options = options.toBuilder() + ResourceManagerOptions otherOptions = options.toBuilder() .projectId("some-unnecessary-project-ID") - .retryParams(RetryParams.defaultInstance()) .build(); - serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - } - - @Test - public void testModelAndRequests() throws Exception { - Serializable[] objects = {PARTIAL_PROJECT_INFO, FULL_PROJECT_INFO, PROJECT, PAGE_RESULT, - PROJECT_GET_OPTION, PROJECT_LIST_OPTION, POLICY}; - for (Serializable obj : objects) { - Object copy = serializeAndDeserialize(obj); - assertEquals(obj, obj); - assertEquals(obj, copy); - assertNotSame(obj, copy); - assertEquals(copy, copy); - } + return new Serializable[]{PARTIAL_PROJECT_INFO, FULL_PROJECT_INFO, PROJECT, PAGE_RESULT, + PROJECT_GET_OPTION, PROJECT_LIST_OPTION, POLICY, RESOURCE_MANAGER_EXCEPTION, options, + otherOptions}; } - @SuppressWarnings("unchecked") - private T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { - output.writeObject(obj); - } - try (ObjectInputStream input = - new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { - return (T) input.readObject(); - } + @Override + protected Restorable[] restorableObjects() { + return null; } } diff --git a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java similarity index 87% rename from gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java rename to gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java index c9b2970a4efa..cf65b4981bfc 100644 --- a/gcloud-java-resourcemanager/src/test/java/com/google/gcloud/resourcemanager/LocalResourceManagerHelperTest.java +++ b/gcloud-java-resourcemanager/src/test/java/com/google/cloud/resourcemanager/testing/LocalResourceManagerHelperTest.java @@ -1,25 +1,30 @@ -package com.google.gcloud.resourcemanager; +package com.google.cloud.resourcemanager.testing; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; 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.api.services.cloudresourcemanager.model.Binding; +import com.google.cloud.resourcemanager.ResourceManagerException; +import com.google.cloud.resourcemanager.spi.DefaultResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc; +import com.google.cloud.resourcemanager.spi.ResourceManagerRpc.Tuple; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.resourcemanager.spi.DefaultResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc; -import com.google.gcloud.resourcemanager.spi.ResourceManagerRpc.Tuple; -import com.google.gcloud.resourcemanager.testing.LocalResourceManagerHelper; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; public class LocalResourceManagerHelperTest { @@ -45,7 +50,12 @@ public class LocalResourceManagerHelperTest { .setLabels(ImmutableMap.of("k1", "v1", "k2", "v2")); private static final com.google.api.services.cloudresourcemanager.model.Project PROJECT_WITH_PARENT = - copyFrom(COMPLETE_PROJECT).setProjectId("project-with-parent-id").setParent(PARENT); + copyFrom(COMPLETE_PROJECT).setProjectId("project-with-parent-id").setParent(PARENT); + private static final List BINDINGS = ImmutableList.of( + new Binding().setRole("roles/owner").setMembers(ImmutableList.of("user:me@gmail.com")), + new Binding().setRole("roles/viewer").setMembers(ImmutableList.of("group:group@gmail.com"))); + private static final com.google.api.services.cloudresourcemanager.model.Policy POLICY = + new com.google.api.services.cloudresourcemanager.model.Policy().setBindings(BINDINGS); @BeforeClass public static void beforeClass() { @@ -92,6 +102,13 @@ public void testCreate() { assertNull(returnedProject.getParent()); assertNotNull(returnedProject.getProjectNumber()); assertNotNull(returnedProject.getCreateTime()); + com.google.api.services.cloudresourcemanager.model.Policy policy = + rpc.getPolicy(PARTIAL_PROJECT.getProjectId()); + assertEquals(Collections.emptyList(), policy.getBindings()); + assertNotNull(policy.getEtag()); + assertEquals(0, policy.getVersion().intValue()); + rpc.replacePolicy(PARTIAL_PROJECT.getProjectId(), POLICY); + assertEquals(POLICY.getBindings(), rpc.getPolicy(PARTIAL_PROJECT.getProjectId()).getBindings()); try { rpc.create(PARTIAL_PROJECT); fail("Should fail, project already exists."); @@ -99,6 +116,8 @@ public void testCreate() { assertEquals(409, e.code()); assertTrue(e.getMessage().startsWith("A project with the same project ID") && e.getMessage().endsWith("already exists.")); + assertEquals( + POLICY.getBindings(), rpc.getPolicy(PARTIAL_PROJECT.getProjectId()).getBindings()); } returnedProject = rpc.create(PROJECT_WITH_PARENT); compareReadWriteFields(PROJECT_WITH_PARENT, returnedProject); @@ -609,6 +628,58 @@ public void testUndeleteWhenDeleteInProgress() { } } + @Test + public void testGetPolicy() { + assertNull(rpc.getPolicy("nonexistent-project")); + rpc.create(PARTIAL_PROJECT); + com.google.api.services.cloudresourcemanager.model.Policy policy = + rpc.getPolicy(PARTIAL_PROJECT.getProjectId()); + assertEquals(Collections.emptyList(), policy.getBindings()); + assertNotNull(policy.getEtag()); + } + + @Test + public void testReplacePolicy() { + try { + rpc.replacePolicy("nonexistent-project", POLICY); + fail("Project doesn't exist."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertTrue(e.getMessage().contains("project was not found")); + } + rpc.create(PARTIAL_PROJECT); + com.google.api.services.cloudresourcemanager.model.Policy invalidPolicy = + new com.google.api.services.cloudresourcemanager.model.Policy().setEtag("wrong-etag"); + try { + rpc.replacePolicy(PARTIAL_PROJECT.getProjectId(), invalidPolicy); + fail("Invalid etag."); + } catch (ResourceManagerException e) { + assertEquals(409, e.code()); + assertTrue(e.getMessage().startsWith("Policy etag mismatch")); + } + String originalEtag = rpc.getPolicy(PARTIAL_PROJECT.getProjectId()).getEtag(); + com.google.api.services.cloudresourcemanager.model.Policy newPolicy = + rpc.replacePolicy(PARTIAL_PROJECT.getProjectId(), POLICY); + assertEquals(POLICY.getBindings(), newPolicy.getBindings()); + assertNotNull(newPolicy.getEtag()); + assertNotEquals(originalEtag, newPolicy.getEtag()); + } + + @Test + public void testTestPermissions() { + List permissions = ImmutableList.of("resourcemanager.projects.get"); + try { + rpc.testPermissions("nonexistent-project", permissions); + fail("Nonexistent project."); + } catch (ResourceManagerException e) { + assertEquals(403, e.code()); + assertEquals("Project nonexistent-project not found.", e.getMessage()); + } + rpc.create(PARTIAL_PROJECT); + assertEquals(ImmutableList.of(true), + rpc.testPermissions(PARTIAL_PROJECT.getProjectId(), permissions)); + } + @Test public void testChangeLifecycleStatus() { assertFalse(RESOURCE_MANAGER_HELPER.changeLifecycleState( @@ -632,8 +703,10 @@ public void testChangeLifecycleStatus() { public void testRemoveProject() { assertFalse(RESOURCE_MANAGER_HELPER.removeProject(COMPLETE_PROJECT.getProjectId())); rpc.create(COMPLETE_PROJECT); + assertNotNull(rpc.getPolicy(COMPLETE_PROJECT.getProjectId())); assertTrue(RESOURCE_MANAGER_HELPER.removeProject(COMPLETE_PROJECT.getProjectId())); assertNull(rpc.get(COMPLETE_PROJECT.getProjectId(), EMPTY_RPC_OPTIONS)); + assertNull(rpc.getPolicy(COMPLETE_PROJECT.getProjectId())); } private void compareReadWriteFields( diff --git a/gcloud-java-storage/README.md b/gcloud-java-storage/README.md index 0ee05b31c10c..bcf7c7068774 100644 --- a/gcloud-java-storage/README.md +++ b/gcloud-java-storage/README.md @@ -5,12 +5,12 @@ Java idiomatic client for [Google Cloud Storage] (https://cloud.google.com/stora [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-storage.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java-storage.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-storage.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java-storage.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) - [Homepage] (https://googlecloudplatform.github.io/gcloud-java/) -- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/storage/package-summary.html) +- [API Documentation] (http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html) > Note: This client is a work-in-progress, and may occasionally > make backwards-incompatible changes. @@ -20,24 +20,24 @@ Quickstart If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java-storage - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java-storage:0.1.5' +compile 'com.google.cloud:gcloud-java-storage:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java-storage" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java-storage" % "0.2.0" ``` Example Application ------------------- -[`StorageExample`](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/StorageExample.java) is a simple command line interface that provides some of Cloud Storage's functionality. Read more about using the application on the [`StorageExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/storage/StorageExample.html). +[`StorageExample`](../gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java) is a simple command line interface that provides some of Cloud Storage's functionality. Read more about using the application on the [`StorageExample` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/cloud/examples/storage/StorageExample.html). Authentication -------------- @@ -61,7 +61,7 @@ with the Cloud Storage using this Client Library. Getting Started --------------- #### Prerequisites -For this tutorial, you will need a [Google Developers Console](https://console.developers.google.com/) project with the Storage JSON API enabled. You will need to [enable billing](https://support.google.com/cloud/answer/6158867?hl=en) to use Google Cloud Storage. [Follow these instructions](https://cloud.google.com/docs/authentication#preparation) to get your project set up. 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` and `gcloud config set project [YOUR PROJECT ID]`. +For this tutorial, you will need a [Google Developers Console](https://console.developers.google.com/) project with "Google Cloud Storage" and "Google Cloud Storage JSON API" enabled via the console's API Manager. You will need to [enable billing](https://support.google.com/cloud/answer/6158867?hl=en) to use Google Cloud Storage. [Follow these instructions](https://cloud.google.com/docs/authentication#preparation) to get your project set up. 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` and `gcloud config set project [YOUR PROJECT ID]`. #### Installation and setup You'll need to obtain the `gcloud-java-storage` library. See the [Quickstart](#quickstart) section to add `gcloud-java-storage` as a dependency in your code. @@ -70,8 +70,8 @@ You'll need to obtain the `gcloud-java-storage` library. See the [Quickstart](# To make authenticated requests to Google Cloud Storage, you must create a service object with credentials. You can then make API calls by calling methods on the Storage 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.storage.Storage; -import com.google.gcloud.storage.StorageOptions; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; Storage storage = StorageOptions.defaultInstance().service(); ``` @@ -86,9 +86,9 @@ Add the following imports at the top of your file: ```java import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.Bucket; -import com.google.gcloud.storage.BucketInfo; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; ``` Then add the following code to create a bucket and upload a simple blob. @@ -143,7 +143,7 @@ while (blobIterator.hasNext()) { #### Complete source code In -[CreateAndListBucketsAndBlobs.java](../gcloud-java-examples/src/main/java/com/google/gcloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java) +[CreateAndListBucketsAndBlobs.java](../gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/CreateAndListBucketsAndBlobs.java) we put together all the code shown above into one program. The program assumes that you are running on Compute Engine or from your own desktop. To run the example on App Engine, simply move the code from the main method to your application's servlet class and change the print statements to @@ -199,5 +199,5 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-storage]: https://cloud.google.com/storage/ [cloud-storage-docs]: https://cloud.google.com/storage/docs/overview [cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets -[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/storage/package-summary.html +[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html [cloud-storage-activation]:https://cloud.google.com/storage/docs/signup?hl=en diff --git a/gcloud-java-storage/pom.xml b/gcloud-java-storage/pom.xml index d5f0f6d98660..1106c7479be7 100644 --- a/gcloud-java-storage/pom.xml +++ b/gcloud-java-storage/pom.xml @@ -4,13 +4,14 @@ gcloud-java-storage jar GCloud Java storage + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-storage Java idiomatic client for Google Cloud Storage. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT gcloud-java-storage @@ -37,6 +38,13 @@ + + ${project.groupId} + gcloud-java-core + ${project.version} + test-jar + test + junit junit diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Acl.java similarity index 99% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Acl.java index 4203d79351b7..1063b24580b8 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Acl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import com.google.api.services.storage.model.BucketAccessControl; import com.google.api.services.storage.model.ObjectAccessControl; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchRequest.java similarity index 94% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchRequest.java index bf77c731754e..05dfe8e2a1dc 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchRequest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; +import com.google.cloud.storage.Storage.BlobGetOption; +import com.google.cloud.storage.Storage.BlobSourceOption; +import com.google.cloud.storage.Storage.BlobTargetOption; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; -import com.google.gcloud.storage.Storage.BlobGetOption; -import com.google.gcloud.storage.Storage.BlobSourceOption; -import com.google.gcloud.storage.Storage.BlobTargetOption; import java.io.Serializable; import java.util.LinkedHashMap; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchResponse.java similarity index 93% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchResponse.java index fe5f6f5743c8..d1e56758b9d2 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchResponse.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BatchResponse.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; @@ -26,7 +26,7 @@ /** * Google Storage batch response. */ -public final class BatchResponse implements Serializable { +public class BatchResponse implements Serializable { private static final long serialVersionUID = 1057416839397037706L; @@ -121,13 +121,16 @@ static Result empty() { } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(deleteResult, updateResult, getResult); } @Override - public boolean equals(Object obj) { - if (!(obj instanceof BatchResponse)) { + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(BatchResponse.class)) { return false; } BatchResponse other = (BatchResponse) obj; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java similarity index 79% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java index dd5d165a72b6..9b74b686d8f9 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java @@ -14,22 +14,27 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; +import static com.google.cloud.storage.Blob.BlobSourceOption.toGetOptions; +import static com.google.cloud.storage.Blob.BlobSourceOption.toSourceOptions; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.Blob.BlobSourceOption.toGetOptions; -import static com.google.gcloud.storage.Blob.BlobSourceOption.toSourceOptions; import com.google.api.services.storage.model.StorageObject; +import com.google.cloud.AuthCredentials; +import com.google.cloud.AuthCredentials.AppEngineAuthCredentials; +import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials; +import com.google.cloud.ReadChannel; +import com.google.cloud.ServiceAccountSigner; +import com.google.cloud.ServiceAccountSigner.SigningException; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.Storage.BlobTargetOption; +import com.google.cloud.storage.Storage.BlobWriteOption; +import com.google.cloud.storage.Storage.CopyRequest; +import com.google.cloud.storage.Storage.SignUrlOption; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.Tuple; import com.google.common.base.Function; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.Storage.BlobTargetOption; -import com.google.gcloud.storage.Storage.BlobWriteOption; -import com.google.gcloud.storage.Storage.CopyRequest; -import com.google.gcloud.storage.Storage.SignUrlOption; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; import java.io.IOException; import java.io.ObjectInputStream; @@ -177,8 +182,8 @@ public Builder blobId(BlobId blobId) { } @Override - Builder id(String id) { - infoBuilder.id(id); + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); return this; } @@ -453,16 +458,54 @@ public WriteChannel writer(BlobWriteOption... options) { } /** - * Generates a signed URL for this blob. If you want to allow access to for a fixed amount of time - * for this blob, you can use this method to generate a URL that is only valid within a certain - * time period. This is particularly useful if you don't want publicly accessible blobs, but don't - * want to require users to explicitly log in. + * Generates a signed URL for this blob. If you want to allow access for a fixed amount of time to + * this blob, you can use this method to generate a URL that is only valid within a certain time + * period. This is particularly useful if you don't want publicly accessible blobs, but also don't + * want to require users to explicitly log in. Signing a URL requires + * a service account signer. If a {@link ServiceAccountAuthCredentials} or an + * {@link AppEngineAuthCredentials} was passed to + * {@link StorageOptions.Builder#authCredentials(AuthCredentials)} or the default credentials are + * being used and the environment variable {@code GOOGLE_APPLICATION_CREDENTIALS} is set, then + * {@code signUrl} will use that credentials to sign the URL. If the credentials passed to + * {@link StorageOptions} do not implement {@link ServiceAccountSigner} (this is the case for + * Compute Engine credentials and Google Cloud SDK credentials) then {@code signUrl} will throw an + * {@link IllegalStateException} unless an implementation of {@link ServiceAccountSigner} is + * passed using the {@link SignUrlOption#signWith(ServiceAccountSigner)} option. + * + *

    A service account signer is looked for in the following order: + *

      + *
    1. The signer passed with the option {@link SignUrlOption#signWith(ServiceAccountSigner)} + *
    2. The credentials passed to {@link StorageOptions.Builder#authCredentials(AuthCredentials)} + *
    3. The default credentials, if no credentials were passed to {@link StorageOptions} + *
    + * + *

    Example usage of creating a signed URL that is valid for 2 weeks, using the default + * credentials for signing the URL: + *

     {@code
    +   * blob.signUrl(14, TimeUnit.DAYS);
    +   * }
    + * + *

    Example usage of creating a signed URL passing the + * {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used for signing the + * URL: + *

     {@code
    +   * blob.signUrl(14, TimeUnit.DAYS, SignUrlOption.signWith(
    +   *     AuthCredentials.createForJson(new FileInputStream("/path/to/key.json"))));
    +   * }
    * * @param duration time until the signed URL expires, expressed in {@code unit}. The finer * granularity supported is 1 second, finer granularities will be truncated * @param unit time unit of the {@code duration} parameter * @param options optional URL signing options - * @return a signed URL for this bucket and the specified options + * @return a signed URL for this blob and the specified options + * @throws IllegalStateException if {@link SignUrlOption#signWith(ServiceAccountSigner)} was not + * used and no implementation of {@link ServiceAccountSigner} was provided to + * {@link StorageOptions} + * @throws IllegalArgumentException if {@code SignUrlOption.withMd5()} option is used and + * {@code blobInfo.md5()} is {@code null} + * @throws IllegalArgumentException if {@code SignUrlOption.withContentType()} option is used and + * {@code blobInfo.contentType()} is {@code null} + * @throws SigningException if the attempt to sign the URL failed * @see Signed-URLs */ public URL signUrl(long duration, TimeUnit unit, SignUrlOption... options) { @@ -483,9 +526,15 @@ public Builder toBuilder() { @Override public final boolean equals(Object obj) { - return (this == obj) || ( - obj instanceof Blob && Objects.equals(toPb(), ((Blob) obj).toPb()) - && Objects.equals(options, ((Blob) obj).options)); + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Blob.class)) { + return false; + } + Blob other = (Blob) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobId.java similarity index 90% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobId.java index d30003d632db..88664e191f84 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobId.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.Preconditions.checkNotNull; @@ -79,9 +79,16 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj instanceof BlobId && Objects.equals(bucket, ((BlobId) obj).bucket) - && Objects.equals(name, ((BlobId) obj).name) - && Objects.equals(generation, ((BlobId) obj).generation); + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(BlobId.class)) { + return false; + } + BlobId other = (BlobId) obj; + return Objects.equals(bucket, other.bucket) + && Objects.equals(name, other.name) + && Objects.equals(generation, other.generation); } StorageObject toPb() { diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobInfo.java similarity index 97% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobInfo.java index cf509c8f0961..f7d21b09a8ee 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; @@ -57,9 +57,9 @@ public StorageObject apply(BlobInfo blobInfo) { } }; - private static final long serialVersionUID = 2228487739943277159L; + private static final long serialVersionUID = -5625857076205028976L; private final BlobId blobId; - private final String id; + private final String generatedId; private final String selfLink; private final String cacheControl; private final List acl; @@ -101,7 +101,7 @@ public abstract static class Builder { */ public abstract Builder blobId(BlobId blobId); - abstract Builder id(String id); + abstract Builder generatedId(String generatedId); /** * Sets the blob's data content type. @@ -199,7 +199,7 @@ public abstract static class Builder { static final class BuilderImpl extends Builder { private BlobId blobId; - private String id; + private String generatedId; private String contentType; private String contentEncoding; private String contentDisposition; @@ -226,7 +226,7 @@ static final class BuilderImpl extends Builder { BuilderImpl(BlobInfo blobInfo) { blobId = blobInfo.blobId; - id = blobInfo.id; + generatedId = blobInfo.generatedId; cacheControl = blobInfo.cacheControl; contentEncoding = blobInfo.contentEncoding; contentType = blobInfo.contentType; @@ -255,8 +255,8 @@ public Builder blobId(BlobId blobId) { } @Override - Builder id(String id) { - this.id = id; + Builder generatedId(String generatedId) { + this.generatedId = generatedId; return this; } @@ -384,7 +384,7 @@ public BlobInfo build() { BlobInfo(BuilderImpl builder) { blobId = builder.blobId; - id = builder.id; + generatedId = builder.generatedId; cacheControl = builder.cacheControl; contentEncoding = builder.contentEncoding; contentType = builder.contentType; @@ -421,10 +421,10 @@ public String bucket() { } /** - * Returns the blob's id. + * Returns the service-generated for the blob. */ - public String id() { - return id; + public String generatedId() { + return generatedId; } /** @@ -638,7 +638,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj != null && obj.getClass().equals(BlobInfo.class) + return obj == this + || obj != null + && obj.getClass().equals(BlobInfo.class) && Objects.equals(toPb(), ((BlobInfo) obj).toPb()); } @@ -684,7 +686,7 @@ public ObjectAccessControl apply(Acl acl) { storageObject.setComponentCount(componentCount); storageObject.setContentLanguage(contentLanguage); storageObject.setEtag(etag); - storageObject.setId(id); + storageObject.setId(generatedId); storageObject.setSelfLink(selfLink); return storageObject; } @@ -757,7 +759,7 @@ static BlobInfo fromPb(StorageObject storageObject) { builder.etag(storageObject.getEtag()); } if (storageObject.getId() != null) { - builder.id(storageObject.getId()); + builder.generatedId(storageObject.getId()); } if (storageObject.getSelfLink() != null) { builder.selfLink(storageObject.getSelfLink()); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannel.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java similarity index 96% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannel.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java index f9c6f912563d..49ceb99a5792 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobReadChannel.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobReadChannel.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.RetryHelper.runWithRetries; +import static com.google.cloud.RetryHelper.runWithRetries; import com.google.api.services.storage.model.StorageObject; +import com.google.cloud.ReadChannel; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryHelper; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.Tuple; import com.google.common.base.MoreObjects; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; import java.io.IOException; import java.io.Serializable; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriteChannel.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java similarity index 89% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriteChannel.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java index 30b0ec870f51..0076b3cbaac4 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobWriteChannel.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BlobWriteChannel.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.RetryHelper.runWithRetries; +import static com.google.cloud.RetryHelper.runWithRetries; import static java.util.concurrent.Executors.callable; -import com.google.gcloud.BaseWriteChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.spi.StorageRpc; +import com.google.cloud.BaseWriteChannel; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryHelper; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.spi.StorageRpc; import java.util.Map; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Bucket.java similarity index 89% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Bucket.java index 5df305ff371c..9f5a2e2499a0 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Bucket.java @@ -14,21 +14,20 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; +import static com.google.cloud.storage.Bucket.BucketSourceOption.toGetOptions; +import static com.google.cloud.storage.Bucket.BucketSourceOption.toSourceOptions; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.Bucket.BucketSourceOption.toGetOptions; -import static com.google.gcloud.storage.Bucket.BucketSourceOption.toSourceOptions; +import com.google.cloud.Page; +import com.google.cloud.storage.Storage.BlobGetOption; +import com.google.cloud.storage.Storage.BucketTargetOption; +import com.google.cloud.storage.spi.StorageRpc; import com.google.common.base.Function; -import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.gcloud.Page; -import com.google.gcloud.storage.Storage.BlobGetOption; -import com.google.gcloud.storage.Storage.BucketTargetOption; -import com.google.gcloud.storage.spi.StorageRpc; import java.io.IOException; import java.io.InputStream; @@ -50,7 +49,7 @@ * {@link BucketInfo}. *

    */ -public final class Bucket extends BucketInfo { +public class Bucket extends BucketInfo { private static final long serialVersionUID = 8574601739542252586L; @@ -428,8 +427,8 @@ public Builder name(String name) { } @Override - Builder id(String id) { - infoBuilder.id(id); + Builder generatedId(String generatedId) { + infoBuilder.generatedId(generatedId); return this; } @@ -627,21 +626,19 @@ public List get(String blobName1, String blobName2, String... blobNames) { /** * Creates a new blob in this bucket. Direct upload is used to upload {@code content}. - * For large content, {@link Blob#writer(com.google.gcloud.storage.Storage.BlobWriteOption...)} + * For large content, {@link Blob#writer(com.google.cloud.storage.Storage.BlobWriteOption...)} * is recommended as it uses resumable upload. MD5 and CRC32C hashes of {@code content} are * computed and used for validating transferred data. * * @param blob a blob name * @param content the blob content - * @param contentType the blob content type. If {@code null} then - * {@value com.google.gcloud.storage.Storage#DEFAULT_CONTENT_TYPE} is used. + * @param contentType the blob content type * @param options options for blob creation * @return a complete blob information * @throws StorageException upon failure */ public Blob create(String blob, byte[] content, String contentType, BlobTargetOption... options) { - BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)) - .contentType(MoreObjects.firstNonNull(contentType, Storage.DEFAULT_CONTENT_TYPE)).build(); + BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)).contentType(contentType).build(); StorageRpc.Tuple target = BlobTargetOption.toTargetOptions(blobInfo, options); return storage.create(target.x(), content, target.y()); @@ -649,21 +646,56 @@ public Blob create(String blob, byte[] content, String contentType, BlobTargetOp /** * Creates a new blob in this bucket. Direct upload is used to upload {@code content}. - * For large content, {@link Blob#writer(com.google.gcloud.storage.Storage.BlobWriteOption...)} + * For large content, {@link Blob#writer(com.google.cloud.storage.Storage.BlobWriteOption...)} * is recommended as it uses resumable upload. * * @param blob a blob name * @param content the blob content as a stream - * @param contentType the blob content type. If {@code null} then - * {@value com.google.gcloud.storage.Storage#DEFAULT_CONTENT_TYPE} is used. + * @param contentType the blob content type * @param options options for blob creation * @return a complete blob information * @throws StorageException upon failure */ public Blob create(String blob, InputStream content, String contentType, BlobWriteOption... options) { - BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)) - .contentType(MoreObjects.firstNonNull(contentType, Storage.DEFAULT_CONTENT_TYPE)).build(); + BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)).contentType(contentType).build(); + StorageRpc.Tuple write = + BlobWriteOption.toWriteOptions(blobInfo, options); + return storage.create(write.x(), content, write.y()); + } + + /** + * Creates a new blob in this bucket. Direct upload is used to upload {@code content}. + * For large content, {@link Blob#writer(com.google.cloud.storage.Storage.BlobWriteOption...)} + * is recommended as it uses resumable upload. MD5 and CRC32C hashes of {@code content} are + * computed and used for validating transferred data. + * + * @param blob a blob name + * @param content the blob content + * @param options options for blob creation + * @return a complete blob information + * @throws StorageException upon failure + */ + public Blob create(String blob, byte[] content, BlobTargetOption... options) { + BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)).build(); + StorageRpc.Tuple target = + BlobTargetOption.toTargetOptions(blobInfo, options); + return storage.create(target.x(), content, target.y()); + } + + /** + * Creates a new blob in this bucket. Direct upload is used to upload {@code content}. + * For large content, {@link Blob#writer(com.google.cloud.storage.Storage.BlobWriteOption...)} + * is recommended as it uses resumable upload. + * + * @param blob a blob name + * @param content the blob content as a stream + * @param options options for blob creation + * @return a complete blob information + * @throws StorageException upon failure + */ + public Blob create(String blob, InputStream content, BlobWriteOption... options) { + BlobInfo blobInfo = BlobInfo.builder(BlobId.of(name(), blob)).build(); StorageRpc.Tuple write = BlobWriteOption.toWriteOptions(blobInfo, options); return storage.create(write.x(), content, write.y()); @@ -682,13 +714,20 @@ public Builder toBuilder() { } @Override - public boolean equals(Object obj) { - return obj instanceof Bucket && Objects.equals(toPb(), ((Bucket) obj).toPb()) - && Objects.equals(options, ((Bucket) obj).options); + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj != null && !obj.getClass().equals(Bucket.class)) { + return false; + } + Bucket other = (Bucket) obj; + return Objects.equals(toPb(), other.toPb()) + && Objects.equals(options, other.options); } @Override - public int hashCode() { + public final int hashCode() { return Objects.hash(super.hashCode(), options); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BucketInfo.java similarity index 97% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/BucketInfo.java index a1de1a07e03e..36e0ed54ffa0 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/BucketInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; @@ -30,10 +30,10 @@ import com.google.api.services.storage.model.Bucket.Website; import com.google.api.services.storage.model.BucketAccessControl; import com.google.api.services.storage.model.ObjectAccessControl; +import com.google.cloud.storage.Acl.Entity; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; -import com.google.gcloud.storage.Acl.Entity; import java.io.IOException; import java.io.ObjectInputStream; @@ -64,8 +64,8 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo) return bucketInfo.toPb(); } }; - private static final long serialVersionUID = -3946094202176916586L; - private final String id; + private static final long serialVersionUID = -4712013629621638459L; + private final String generatedId; private final String name; private final Acl.Entity owner; private final String selfLink; @@ -326,7 +326,7 @@ public abstract static class Builder { */ public abstract Builder name(String name); - abstract Builder id(String id); + abstract Builder generatedId(String generatedId); abstract Builder owner(Acl.Entity owner); @@ -411,7 +411,7 @@ public abstract static class Builder { static final class BuilderImpl extends Builder { - private String id; + private String generatedId; private String name; private Acl.Entity owner; private String selfLink; @@ -433,7 +433,7 @@ static final class BuilderImpl extends Builder { } BuilderImpl(BucketInfo bucketInfo) { - id = bucketInfo.id; + generatedId = bucketInfo.generatedId; name = bucketInfo.name; etag = bucketInfo.etag; createTime = bucketInfo.createTime; @@ -458,8 +458,8 @@ public Builder name(String name) { } @Override - Builder id(String id) { - this.id = id; + Builder generatedId(String generatedId) { + this.generatedId = generatedId; return this; } @@ -555,7 +555,7 @@ public BucketInfo build() { } BucketInfo(BuilderImpl builder) { - id = builder.id; + generatedId = builder.generatedId; name = builder.name; etag = builder.etag; createTime = builder.createTime; @@ -574,10 +574,10 @@ public BucketInfo build() { } /** - * Returns the bucket's id. + * Returns the service-generated id for the bucket. */ - public String id() { - return id; + public String generatedId() { + return generatedId; } /** @@ -719,7 +719,9 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return obj != null && obj.getClass().equals(BucketInfo.class) + return obj == this + || obj != null + && obj.getClass().equals(BucketInfo.class) && Objects.equals(toPb(), ((BucketInfo) obj).toPb()); } @@ -733,7 +735,7 @@ public String toString() { com.google.api.services.storage.model.Bucket toPb() { com.google.api.services.storage.model.Bucket bucketPb = new com.google.api.services.storage.model.Bucket(); - bucketPb.setId(id); + bucketPb.setId(generatedId); bucketPb.setName(name); bucketPb.setEtag(etag); if (createTime != null) { @@ -810,7 +812,7 @@ public static Builder builder(String name) { static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb) { Builder builder = new BuilderImpl(bucketPb.getName()); if (bucketPb.getId() != null) { - builder.id(bucketPb.getId()); + builder.generatedId(bucketPb.getId()); } if (bucketPb.getEtag() != null) { builder.etag(bucketPb.getEtag()); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/CopyWriter.java similarity index 78% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/CopyWriter.java index 62b39e005369..9e674052f877 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/CopyWriter.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.RetryHelper.runWithRetries; +import static com.google.cloud.RetryHelper.runWithRetries; +import com.google.cloud.Restorable; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryHelper; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.RewriteRequest; +import com.google.cloud.storage.spi.StorageRpc.RewriteResponse; import com.google.common.base.MoreObjects; -import com.google.gcloud.Restorable; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryHelper; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.RewriteRequest; -import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse; import java.io.Serializable; import java.util.Map; @@ -32,7 +32,13 @@ import java.util.concurrent.Callable; /** - * Google Storage blob copy writer. This class holds the result of a copy request. If source and + * Google Storage blob copy writer. A {@code CopyWriter} object allows to copy both blob's data and + * information. To override source blob's information supply a {@code BlobInfo} to the + * {@code CopyRequest} using either + * {@link Storage.CopyRequest.Builder#target(BlobInfo, Storage.BlobTargetOption...)} or + * {@link Storage.CopyRequest.Builder#target(BlobInfo, Iterable)}. + * + *

    This class holds the result of a copy request. If source and * destination blobs share the same location and storage class the copy is completed in one RPC call * otherwise one or more {@link #copyChunk} calls are necessary to complete the copy. In addition, * {@link CopyWriter#result()} can be used to automatically complete the copy and return information @@ -65,11 +71,11 @@ public class CopyWriter implements Restorable { * * @throws StorageException upon failure */ - public BlobInfo result() { + public Blob result() { while (!isDone()) { copyChunk(); } - return BlobInfo.fromPb(rewriteResponse.result); + return Blob.fromPb(serviceOptions.service(), rewriteResponse.result); } /** @@ -120,8 +126,10 @@ public RestorableState capture() { serviceOptions, BlobId.fromPb(rewriteResponse.rewriteRequest.source), rewriteResponse.rewriteRequest.sourceOptions, + rewriteResponse.rewriteRequest.overrideInfo, BlobInfo.fromPb(rewriteResponse.rewriteRequest.target), rewriteResponse.rewriteRequest.targetOptions) + .result(rewriteResponse.result != null ? BlobInfo.fromPb(rewriteResponse.result) : null) .blobSize(blobSize()) .isDone(isDone()) .megabytesCopiedPerChunk(rewriteResponse.rewriteRequest.megabytesRewrittenPerCall) @@ -132,11 +140,12 @@ public RestorableState capture() { static class StateImpl implements RestorableState, Serializable { - private static final long serialVersionUID = 8279287678903181701L; + private static final long serialVersionUID = 1693964441435822700L; private final StorageOptions serviceOptions; private final BlobId source; private final Map sourceOptions; + private final boolean overrideInfo; private final BlobInfo target; private final Map targetOptions; private final BlobInfo result; @@ -150,6 +159,7 @@ static class StateImpl implements RestorableState, Serializable { this.serviceOptions = builder.serviceOptions; this.source = builder.source; this.sourceOptions = builder.sourceOptions; + this.overrideInfo = builder.overrideInfo; this.target = builder.target; this.targetOptions = builder.targetOptions; this.result = builder.result; @@ -165,6 +175,7 @@ static class Builder { private final StorageOptions serviceOptions; private final BlobId source; private final Map sourceOptions; + private final boolean overrideInfo; private final BlobInfo target; private final Map targetOptions; private BlobInfo result; @@ -175,11 +186,12 @@ static class Builder { private Long megabytesCopiedPerChunk; private Builder(StorageOptions options, BlobId source, - Map sourceOptions, - BlobInfo target, Map targetOptions) { + Map sourceOptions, boolean overrideInfo, BlobInfo target, + Map targetOptions) { this.serviceOptions = options; this.source = source; this.sourceOptions = sourceOptions; + this.overrideInfo = overrideInfo; this.target = target; this.targetOptions = targetOptions; } @@ -220,15 +232,15 @@ RestorableState build() { } static Builder builder(StorageOptions options, BlobId source, - Map sourceOptions, BlobInfo target, + Map sourceOptions, boolean overrideInfo, BlobInfo target, Map targetOptions) { - return new Builder(options, source, sourceOptions, target, targetOptions); + return new Builder(options, source, sourceOptions, overrideInfo, target, targetOptions); } @Override public CopyWriter restore() { - RewriteRequest rewriteRequest = new RewriteRequest( - source.toPb(), sourceOptions, target.toPb(), targetOptions, megabytesCopiedPerChunk); + RewriteRequest rewriteRequest = new RewriteRequest(source.toPb(), sourceOptions, + overrideInfo, target.toPb(), targetOptions, megabytesCopiedPerChunk); RewriteResponse rewriteResponse = new RewriteResponse(rewriteRequest, result != null ? result.toPb() : null, blobSize, isDone, rewriteToken, totalBytesCopied); @@ -237,8 +249,9 @@ public CopyWriter restore() { @Override public int hashCode() { - return Objects.hash(serviceOptions, source, sourceOptions, target, targetOptions, result, - blobSize, isDone, megabytesCopiedPerChunk, rewriteToken, totalBytesCopied); + return Objects.hash(serviceOptions, source, sourceOptions, overrideInfo, target, + targetOptions, result, blobSize, isDone, megabytesCopiedPerChunk, rewriteToken, + totalBytesCopied); } @Override @@ -253,6 +266,7 @@ public boolean equals(Object obj) { return Objects.equals(this.serviceOptions, other.serviceOptions) && Objects.equals(this.source, other.source) && Objects.equals(this.sourceOptions, other.sourceOptions) + && Objects.equals(this.overrideInfo, other.overrideInfo) && Objects.equals(this.target, other.target) && Objects.equals(this.targetOptions, other.targetOptions) && Objects.equals(this.result, other.result) @@ -267,10 +281,14 @@ public boolean equals(Object obj) { public String toString() { return MoreObjects.toStringHelper(this) .add("source", source) + .add("overrideInfo", overrideInfo) .add("target", target) - .add("isDone", isDone) - .add("totalBytesRewritten", totalBytesCopied) + .add("result", result) .add("blobSize", blobSize) + .add("isDone", isDone) + .add("rewriteToken", rewriteToken) + .add("totalBytesCopied", totalBytesCopied) + .add("megabytesCopiedPerChunk", megabytesCopiedPerChunk) .toString(); } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Cors.java similarity index 99% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Cors.java index bcbbd1030dbc..27ff44156712 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Cors.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.transform; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/HttpMethod.java similarity index 95% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/HttpMethod.java index e2a4563316d3..0d4953b9f316 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/HttpMethod.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/HttpMethod.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; /** * Http method supported by Storage service. diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Option.java similarity index 93% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Option.java index 65c55da7efc8..e15d53a2d591 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Option.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Option.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.storage.spi.StorageRpc; import com.google.common.base.MoreObjects; -import com.google.gcloud.storage.spi.StorageRpc; import java.io.Serializable; import java.util.Objects; @@ -27,7 +27,7 @@ /** * Base class for Storage operation option. */ -class Option implements Serializable { +abstract class Option implements Serializable { private static final long serialVersionUID = -73199088766477208L; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java similarity index 86% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java index 6b2e9266f24b..4c32aea1a428 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -14,30 +14,33 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.Joiner; +import com.google.cloud.AuthCredentials; +import com.google.cloud.AuthCredentials.AppEngineAuthCredentials; +import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials; +import com.google.cloud.FieldSelector; +import com.google.cloud.FieldSelector.Helper; +import com.google.cloud.Page; +import com.google.cloud.ReadChannel; +import com.google.cloud.Service; +import com.google.cloud.ServiceAccountSigner; +import com.google.cloud.ServiceAccountSigner.SigningException; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.Tuple; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; -import com.google.gcloud.Page; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.Service; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; import java.io.InputStream; import java.io.Serializable; import java.net.URL; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -52,8 +55,6 @@ */ public interface Storage extends Service { - String DEFAULT_CONTENT_TYPE = "application/octet-stream"; - enum PredefinedAcl { AUTHENTICATED_READ("authenticatedRead"), ALL_AUTHENTICATED_USERS("allAuthenticatedUsers"), @@ -75,7 +76,7 @@ String entry() { } } - enum BucketField { + enum BucketField implements FieldSelector { ID("id"), SELF_LINK("selfLink"), NAME("name"), @@ -91,27 +92,21 @@ enum BucketField { STORAGE_CLASS("storageClass"), ETAG("etag"); + static final List REQUIRED_FIELDS = ImmutableList.of(NAME); + private final String selector; BucketField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(BucketField... fields) { - HashSet fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); - fieldStrings.add(NAME.selector()); - for (BucketField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } - enum BlobField { + enum BlobField implements FieldSelector { ACL("acl"), BUCKET("bucket"), CACHE_CONTROL("cacheControl"), @@ -137,25 +132,18 @@ enum BlobField { TIME_DELETED("timeDeleted"), UPDATED("updated"); + static final List REQUIRED_FIELDS = ImmutableList.of(BUCKET, NAME); + private final String selector; BlobField(String selector) { this.selector = selector; } + @Override public String selector() { return selector; } - - static String selector(BlobField... fields) { - HashSet fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 2); - fieldStrings.add(BUCKET.selector()); - fieldStrings.add(NAME.selector()); - for (BlobField field : fields) { - fieldStrings.add(field.selector()); - } - return Joiner.on(',').join(fieldStrings); - } } /** @@ -270,7 +258,8 @@ public static BucketGetOption metagenerationNotMatch(long metageneration) { * specified. */ public static BucketGetOption fields(BucketField... fields) { - return new BucketGetOption(StorageRpc.Option.FIELDS, BucketField.selector(fields)); + return new BucketGetOption(StorageRpc.Option.FIELDS, + Helper.selector(BucketField.REQUIRED_FIELDS, fields)); } } @@ -610,7 +599,8 @@ public static BlobGetOption metagenerationNotMatch(long metageneration) { * specified. */ public static BlobGetOption fields(BlobField... fields) { - return new BlobGetOption(StorageRpc.Option.FIELDS, BlobField.selector(fields)); + return new BlobGetOption(StorageRpc.Option.FIELDS, + Helper.selector(BlobField.REQUIRED_FIELDS, fields)); } } @@ -626,16 +616,16 @@ private BucketListOption(StorageRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of buckets to be returned. + * Returns an option to specify the maximum number of buckets returned per page. */ - public static BucketListOption maxResults(long maxResults) { - return new BucketListOption(StorageRpc.Option.MAX_RESULTS, maxResults); + public static BucketListOption pageSize(long pageSize) { + return new BucketListOption(StorageRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing buckets. */ - public static BucketListOption startPageToken(String pageToken) { + public static BucketListOption pageToken(String pageToken) { return new BucketListOption(StorageRpc.Option.PAGE_TOKEN, pageToken); } @@ -654,9 +644,8 @@ public static BucketListOption prefix(String prefix) { * specified. */ public static BucketListOption fields(BucketField... fields) { - StringBuilder builder = new StringBuilder(); - builder.append("items(").append(BucketField.selector(fields)).append("),nextPageToken"); - return new BucketListOption(StorageRpc.Option.FIELDS, builder.toString()); + return new BucketListOption(StorageRpc.Option.FIELDS, + Helper.listSelector("items", BucketField.REQUIRED_FIELDS, fields)); } } @@ -665,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) { @@ -672,16 +662,16 @@ private BlobListOption(StorageRpc.Option option, Object value) { } /** - * Returns an option to specify the maximum number of blobs to be returned. + * Returns an option to specify the maximum number of blobs returned per page. */ - public static BlobListOption maxResults(long maxResults) { - return new BlobListOption(StorageRpc.Option.MAX_RESULTS, maxResults); + public static BlobListOption pageSize(long pageSize) { + return new BlobListOption(StorageRpc.Option.MAX_RESULTS, pageSize); } /** * Returns an option to specify the page token from which to start listing blobs. */ - public static BlobListOption startPageToken(String pageToken) { + public static BlobListOption pageToken(String pageToken) { return new BlobListOption(StorageRpc.Option.PAGE_TOKEN, pageToken); } @@ -723,9 +713,8 @@ public static BlobListOption versions(boolean versions) { * specified. */ public static BlobListOption fields(BlobField... fields) { - StringBuilder builder = new StringBuilder(); - builder.append("items(").append(BlobField.selector(fields)).append("),nextPageToken"); - return new BlobListOption(StorageRpc.Option.FIELDS, builder.toString()); + return new BlobListOption(StorageRpc.Option.FIELDS, + Helper.listSelector(TOP_LEVEL_FIELDS, "items", BlobField.REQUIRED_FIELDS, fields)); } } @@ -780,14 +769,14 @@ public static SignUrlOption withMd5() { } /** - * Service account credentials which are used for signing the URL. - * If not provided an attempt will be made to get it from the environment. + * Provides a service account signer to sign the URL. If not provided an attempt will be made to + * get it from the environment. * * @see Service * account */ - public static SignUrlOption serviceAccount(ServiceAccountAuthCredentials credentials) { - return new SignUrlOption(Option.SERVICE_ACCOUNT_CRED, credentials); + public static SignUrlOption signWith(ServiceAccountSigner signer) { + return new SignUrlOption(Option.SERVICE_ACCOUNT_CRED, signer); } } @@ -963,6 +952,7 @@ class CopyRequest implements Serializable { private final BlobId source; private final List sourceOptions; + private final boolean overrideInfo; private final BlobInfo target; private final List targetOptions; private final Long megabytesCopiedPerChunk; @@ -972,6 +962,7 @@ public static class Builder { private final Set sourceOptions = new LinkedHashSet<>(); private final Set targetOptions = new LinkedHashSet<>(); private BlobId source; + private boolean overrideInfo; private BlobInfo target; private Long megabytesCopiedPerChunk; @@ -1020,39 +1011,38 @@ public Builder sourceOptions(Iterable options) { * * @return the builder */ - public Builder target(BlobId target) { - this.target = BlobInfo.builder(target).build(); + public Builder target(BlobId targetId) { + this.overrideInfo = false; + this.target = BlobInfo.builder(targetId).build(); return this; } /** * Sets the copy target and target options. {@code target} parameter is used to override - * source blob information (e.g. {@code contentType}, {@code contentLanguage}). {@code - * target.contentType} is a required field. + * source blob information (e.g. {@code contentType}, {@code contentLanguage}). Target blob + * information is set exactly to {@code target}, no information is inherited from the source + * blob. * * @return the builder - * @throws IllegalArgumentException if {@code target.contentType} is {@code null} */ - public Builder target(BlobInfo target, BlobTargetOption... options) - throws IllegalArgumentException { - checkContentType(target); - this.target = target; + public Builder target(BlobInfo target, BlobTargetOption... options) { + this.overrideInfo = true; + this.target = checkNotNull(target); Collections.addAll(targetOptions, options); return this; } /** * Sets the copy target and target options. {@code target} parameter is used to override - * source blob information (e.g. {@code contentType}, {@code contentLanguage}). {@code - * target.contentType} is a required field. + * source blob information (e.g. {@code contentType}, {@code contentLanguage}). Target blob + * information is set exactly to {@code target}, no information is inherited from the source + * blob. * * @return the builder - * @throws IllegalArgumentException if {@code target.contentType} is {@code null} */ - public Builder target(BlobInfo target, Iterable options) - throws IllegalArgumentException { - checkContentType(target); - this.target = target; + public Builder target(BlobInfo target, Iterable options) { + this.overrideInfo = true; + this.target = checkNotNull(target); Iterables.addAll(targetOptions, options); return this; } @@ -1073,8 +1063,6 @@ public Builder megabytesCopiedPerChunk(Long megabytesCopiedPerChunk) { * Creates a {@code CopyRequest} object. */ public CopyRequest build() { - checkNotNull(source); - checkNotNull(target); return new CopyRequest(this); } } @@ -1082,6 +1070,7 @@ public CopyRequest build() { private CopyRequest(Builder builder) { source = checkNotNull(builder.source); sourceOptions = ImmutableList.copyOf(builder.sourceOptions); + overrideInfo = builder.overrideInfo; target = checkNotNull(builder.target); targetOptions = ImmutableList.copyOf(builder.targetOptions); megabytesCopiedPerChunk = builder.megabytesCopiedPerChunk; @@ -1108,6 +1097,17 @@ public BlobInfo target() { return target; } + /** + * Returns whether to override the target blob information with {@link #target()}. + * If {@code true}, the value of {@link #target()} is used to replace source blob information + * (e.g. {@code contentType}, {@code contentLanguage}). Target blob information is set exactly + * to this value, no information is inherited from the source blob. If {@code false}, target + * blob information is inherited from the source blob. + */ + public boolean overrideInfo() { + return overrideInfo; + } + /** * Returns blob's target options. */ @@ -1126,34 +1126,27 @@ public Long megabytesCopiedPerChunk() { /** * Creates a copy request. {@code target} parameter is used to override source blob information - * (e.g. {@code contentType}, {@code contentLanguage}). {@code target.contentType} is a required - * field. + * (e.g. {@code contentType}, {@code contentLanguage}). * * @param sourceBucket name of the bucket containing the source blob * @param sourceBlob name of the source blob * @param target a {@code BlobInfo} object for the target blob * @return a copy request - * @throws IllegalArgumentException if {@code target.contentType} is {@code null} */ - public static CopyRequest of(String sourceBucket, String sourceBlob, BlobInfo target) - throws IllegalArgumentException { - checkContentType(target); + public static CopyRequest of(String sourceBucket, String sourceBlob, BlobInfo target) { return builder().source(sourceBucket, sourceBlob).target(target).build(); } /** - * Creates a copy request. {@code target} parameter is used to override source blob information - * (e.g. {@code contentType}, {@code contentLanguage}). {@code target.contentType} is a required - * field. + * Creates a copy request. {@code target} parameter is used to replace source blob information + * (e.g. {@code contentType}, {@code contentLanguage}). Target blob information is set exactly + * to {@code target}, no information is inherited from the source blob. * * @param sourceBlobId a {@code BlobId} object for the source blob * @param target a {@code BlobInfo} object for the target blob * @return a copy request - * @throws IllegalArgumentException if {@code target.contentType} is {@code null} */ - public static CopyRequest of(BlobId sourceBlobId, BlobInfo target) - throws IllegalArgumentException { - checkContentType(target); + public static CopyRequest of(BlobId sourceBlobId, BlobInfo target) { return builder().source(sourceBlobId).target(target).build(); } @@ -1215,10 +1208,6 @@ public static CopyRequest of(BlobId sourceBlobId, BlobId targetBlobId) { public static Builder builder() { return new Builder(); } - - private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentException { - checkArgument(blobInfo.contentType() != null, "Blob content type can not be null"); - } } /** @@ -1386,12 +1375,18 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx Blob compose(ComposeRequest composeRequest); /** - * Sends a copy request. Returns a {@link CopyWriter} object for the provided - * {@code CopyRequest}. If source and destination objects share the same location and storage - * class the source blob is copied with one request and {@link CopyWriter#result()} immediately - * returns, regardless of the {@link CopyRequest#megabytesCopiedPerChunk} parameter. - * If source and destination have different location or storage class {@link CopyWriter#result()} - * might issue multiple RPC calls depending on blob's size. + * Sends a copy request. This method copies both blob's data and information. To override source + * blob's information supply a {@code BlobInfo} to the + * {@code CopyRequest} using either + * {@link Storage.CopyRequest.Builder#target(BlobInfo, Storage.BlobTargetOption...)} or + * {@link Storage.CopyRequest.Builder#target(BlobInfo, Iterable)}. + * + *

    This method returns a {@link CopyWriter} object for the provided {@code CopyRequest}. If + * source and destination objects share the same location and storage class the source blob is + * copied with one request and {@link CopyWriter#result()} immediately returns, regardless of the + * {@link CopyRequest#megabytesCopiedPerChunk} parameter. If source and destination have different + * location or storage class {@link CopyWriter#result()} might issue multiple RPC calls depending + * on blob's size. * *

    Example usage of copy: *

     {@code BlobInfo blob = service.copy(copyRequest).result();}
    @@ -1476,23 +1471,55 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx
       WriteChannel writer(BlobInfo blobInfo, BlobWriteOption... options);
     
       /**
    -   * Generates a signed URL for a blob.
    -   * If you have a blob that you want to allow access to for a fixed
    -   * amount of time, you can use this method to generate a URL that
    -   * is only valid within a certain time period.
    -   * This is particularly useful if you don't want publicly
    -   * accessible blobs, but don't want to require users to explicitly log in.
    +   * Generates a signed URL for a blob. If you have a blob that you want to allow access to for a
    +   * fixed amount of time, you can use this method to generate a URL that is only valid within a
    +   * certain time period. This is particularly useful if you don't want publicly accessible blobs,
    +   * but also don't want to require users to explicitly log in. Signing a URL requires
    +   * a service account signer. If a {@link ServiceAccountAuthCredentials} or an
    +   * {@link AppEngineAuthCredentials} was passed to
    +   * {@link StorageOptions.Builder#authCredentials(AuthCredentials)} or the default credentials are
    +   * being used and the environment variable {@code GOOGLE_APPLICATION_CREDENTIALS} is set, then
    +   * {@code signUrl} will use that credentials to sign the URL. If the credentials passed to
    +   * {@link StorageOptions} do not implement {@link ServiceAccountSigner} (this is the case for
    +   * Compute Engine credentials and Google Cloud SDK credentials) then {@code signUrl} will throw an
    +   * {@link IllegalStateException} unless an implementation of {@link ServiceAccountSigner} is
    +   * passed using the {@link SignUrlOption#signWith(ServiceAccountSigner)} option.
    +   *
    +   * 

    A service account signer is looked for in the following order: + *

      + *
    1. The signer passed with the option {@link SignUrlOption#signWith(ServiceAccountSigner)} + *
    2. The credentials passed to {@link StorageOptions.Builder#authCredentials(AuthCredentials)} + *
    3. The default credentials, if no credentials were passed to {@link StorageOptions} + *
    * - *

    Example usage of creating a signed URL that is valid for 2 weeks: + *

    Example usage of creating a signed URL that is valid for 2 weeks, using the default + * credentials for signing the URL: *

     {@code
        * service.signUrl(BlobInfo.builder("bucket", "name").build(), 14, TimeUnit.DAYS);
        * }
    * + *

    Example usage of creating a signed URL passing the + * {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used for signing the + * URL: + *

     {@code
    +   * service.signUrl(BlobInfo.builder("bucket", "name").build(), 14, TimeUnit.DAYS,
    +   *     SignUrlOption.signWith(
    +   *         AuthCredentials.createForJson(new FileInputStream("/path/to/key.json"))));
    +   * }
    + * * @param blobInfo the blob associated with the signed URL * @param duration time until the signed URL expires, expressed in {@code unit}. The finest * granularity supported is 1 second, finer granularities will be truncated * @param unit time unit of the {@code duration} parameter * @param options optional URL signing options + * @throws IllegalStateException if {@link SignUrlOption#signWith(ServiceAccountSigner)} was not + * used and no implementation of {@link ServiceAccountSigner} was provided to + * {@link StorageOptions} + * @throws IllegalArgumentException if {@code SignUrlOption.withMd5()} option is used and + * {@code blobInfo.md5()} is {@code null} + * @throws IllegalArgumentException if {@code SignUrlOption.withContentType()} option is used and + * {@code blobInfo.contentType()} is {@code null} + * @throws SigningException if the attempt to sign the URL failed * @see Signed-URLs */ URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options); @@ -1512,7 +1539,7 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx * are merged with metadata in the provided {@code BlobInfo} objects. To replace metadata instead * you first have to unset them. Unsetting metadata can be done by setting the provided * {@code BlobInfo} objects metadata to {@code null}. See - * {@link #update(com.google.gcloud.storage.BlobInfo)} for a code example. + * {@link #update(BlobInfo)} for a code example. * * @param blobInfos blobs to update * @return an immutable list of {@code Blob} objects. If a blob does not exist or access to it diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageException.java similarity index 91% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageException.java index ee85b80d6e13..d6333c5c24e3 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageException.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import com.google.api.client.googleapis.json.GoogleJsonError; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.RetryHelper.RetryInterruptedException; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.RetryHelper.RetryInterruptedException; import java.io.IOException; import java.util.Set; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageFactory.java similarity index 90% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageFactory.java index fbce5559464c..0afaa05bcc7c 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageFactory.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageFactory.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import com.google.gcloud.ServiceFactory; +import com.google.cloud.ServiceFactory; /** * An interface for Storage factories. diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageImpl.java similarity index 90% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageImpl.java index d58c9e43aea9..f4769905d8bc 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageImpl.java @@ -14,24 +14,34 @@ * limitations under the License. */ -package com.google.gcloud.storage; - +package com.google.cloud.storage; + +import static com.google.cloud.RetryHelper.runWithRetries; +import static com.google.cloud.storage.spi.StorageRpc.Option.DELIMITER; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH; +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.gcloud.RetryHelper.runWithRetries; -import static com.google.gcloud.storage.spi.StorageRpc.Option.DELIMITER; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH; -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH; +import static com.google.common.base.Preconditions.checkState; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.services.storage.model.StorageObject; -import com.google.auth.oauth2.ServiceAccountCredentials; +import com.google.cloud.BaseService; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.PageImpl.NextPageFetcher; +import com.google.cloud.ReadChannel; +import com.google.cloud.RetryHelper.RetryHelperException; +import com.google.cloud.ServiceAccountSigner; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.RewriteResponse; +import com.google.cloud.storage.spi.StorageRpc.Tuple; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -41,16 +51,6 @@ import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; import com.google.common.primitives.Ints; -import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; -import com.google.gcloud.BaseService; -import com.google.gcloud.Page; -import com.google.gcloud.PageImpl; -import com.google.gcloud.PageImpl.NextPageFetcher; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RetryHelper.RetryHelperException; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -59,10 +59,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.Signature; -import java.security.SignatureException; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -413,15 +409,16 @@ public CopyWriter copy(final CopyRequest copyRequest) { final StorageObject source = copyRequest.source().toPb(); final Map sourceOptions = optionMap(copyRequest.source().generation(), null, copyRequest.sourceOptions(), true); - final StorageObject target = copyRequest.target().toPb(); + final StorageObject targetObject = copyRequest.target().toPb(); final Map targetOptions = optionMap(copyRequest.target().generation(), copyRequest.target().metageneration(), copyRequest.targetOptions()); try { RewriteResponse rewriteResponse = runWithRetries(new Callable() { @Override public RewriteResponse call() { - return storageRpc.openRewrite(new StorageRpc.RewriteRequest(source, sourceOptions, target, - targetOptions, copyRequest.megabytesCopiedPerChunk())); + return storageRpc.openRewrite(new StorageRpc.RewriteRequest(source, sourceOptions, + copyRequest.overrideInfo(), targetObject, targetOptions, + copyRequest.megabytesCopiedPerChunk())); } }, options().retryParams(), EXCEPTION_HANDLER); return new CopyWriter(options(), rewriteResponse); @@ -537,15 +534,12 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio for (SignUrlOption option : options) { optionMap.put(option.option(), option.value()); } - ServiceAccountAuthCredentials authCred = - (ServiceAccountAuthCredentials) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED); - ServiceAccountCredentials cred = authCred != null ? authCred.credentials() : null; - if (authCred == null) { - checkArgument( - this.options().authCredentials() != null - && this.options().authCredentials().credentials() instanceof ServiceAccountCredentials, + ServiceAccountSigner authCredentials = + (ServiceAccountSigner) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED); + if (authCredentials == null) { + checkState(this.options().authCredentials() instanceof ServiceAccountSigner, "Signing key was not provided and could not be derived"); - cred = (ServiceAccountCredentials) this.options().authCredentials().credentials(); + authCredentials = (ServiceAccountSigner) this.options().authCredentials(); } // construct signature - see https://cloud.google.com/storage/docs/access-control#Signed-URLs StringBuilder stBuilder = new StringBuilder(); @@ -582,20 +576,16 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio path.append(blobInfo.name()); stBuilder.append(path); try { - Signature signer = Signature.getInstance("SHA256withRSA"); - signer.initSign(cred.getPrivateKey()); - signer.update(stBuilder.toString().getBytes(UTF_8)); + byte[] signatureBytes = authCredentials.sign(stBuilder.toString().getBytes(UTF_8)); stBuilder = new StringBuilder("https://storage.googleapis.com").append(path); String signature = - URLEncoder.encode(BaseEncoding.base64().encode(signer.sign()), UTF_8.name()); - stBuilder.append("?GoogleAccessId=").append(cred.getClientEmail()); + URLEncoder.encode(BaseEncoding.base64().encode(signatureBytes), UTF_8.name()); + stBuilder.append("?GoogleAccessId=").append(authCredentials.account()); stBuilder.append("&Expires=").append(expiration); stBuilder.append("&Signature=").append(signature); return new URL(stBuilder.toString()); - } catch (MalformedURLException | NoSuchAlgorithmException | UnsupportedEncodingException e) { - throw new IllegalStateException(e); - } catch (SignatureException | InvalidKeyException e) { - throw new IllegalArgumentException("Invalid service account private key"); + } catch (MalformedURLException | UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageOptions.java similarity index 90% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageOptions.java index e7e1c2778fa9..5eeed5c88a69 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/StorageOptions.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; +import com.google.cloud.ServiceOptions; +import com.google.cloud.storage.spi.DefaultStorageRpc; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpcFactory; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.storage.spi.DefaultStorageRpc; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpcFactory; import java.util.Set; @@ -69,13 +69,11 @@ private StorageOptions(Builder builder) { super(StorageFactory.class, StorageRpcFactory.class, builder); } - @SuppressWarnings("unchecked") @Override protected StorageFactory defaultServiceFactory() { return DefaultStorageFactory.INSTANCE; } - @SuppressWarnings("unchecked") @Override protected StorageRpcFactory defaultRpcFactory() { return DefaultStorageRpcFactory.INSTANCE; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/package-info.java similarity index 89% rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/package-info.java index 181e63b08d0b..11ab4465f33c 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/package-info.java @@ -19,7 +19,7 @@ * *

    Here's a simple usage example for using gcloud-java from App/Compute Engine. This example * shows how to create a Storage blob. For the complete source code see - * + * * CreateBlob.java. *

     {@code
      * Storage storage = StorageOptions.defaultInstance().service();
    @@ -30,7 +30,7 @@
      * 

    * This second example shows how to update the blob's content if the blob exists. For the complete * source code see - * + * * UpdateBlob.java. *

     {@code
      * Storage storage = StorageOptions.defaultInstance().service();
    @@ -50,5 +50,5 @@
      * credentials.
      * @see Google Cloud Storage
      */
    -package com.google.gcloud.storage;
    +package com.google.cloud.storage;
     
    diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java
    similarity index 93%
    rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java
    rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java
    index aa6085e161ed..4ef9afc45e4c 100644
    --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/DefaultStorageRpc.java
    +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/DefaultStorageRpc.java
    @@ -12,25 +12,25 @@
      * the License.
      */
     
    -package com.google.gcloud.storage.spi;
    -
    +package com.google.cloud.storage.spi;
    +
    +import static com.google.cloud.storage.spi.StorageRpc.Option.DELIMITER;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.FIELDS;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.MAX_RESULTS;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.PAGE_TOKEN;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.PREDEFINED_ACL;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.PREFIX;
    +import static com.google.cloud.storage.spi.StorageRpc.Option.VERSIONS;
     import static com.google.common.base.MoreObjects.firstNonNull;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.DELIMITER;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.FIELDS;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_METAGENERATION_NOT_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_GENERATION_NOT_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.IF_SOURCE_METAGENERATION_NOT_MATCH;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.MAX_RESULTS;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.PAGE_TOKEN;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.PREDEFINED_ACL;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.PREFIX;
    -import static com.google.gcloud.storage.spi.StorageRpc.Option.VERSIONS;
     import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
     import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
     
    @@ -58,13 +58,13 @@
     import com.google.api.services.storage.model.ComposeRequest.SourceObjects.ObjectPreconditions;
     import com.google.api.services.storage.model.Objects;
     import com.google.api.services.storage.model.StorageObject;
    +import com.google.cloud.storage.StorageException;
    +import com.google.cloud.storage.StorageOptions;
     import com.google.common.base.Function;
     import com.google.common.collect.ImmutableList;
     import com.google.common.collect.Iterables;
     import com.google.common.collect.Lists;
     import com.google.common.collect.Maps;
    -import com.google.gcloud.storage.StorageException;
    -import com.google.gcloud.storage.StorageOptions;
     
     import java.io.ByteArrayOutputStream;
     import java.io.IOException;
    @@ -319,9 +319,6 @@ private Storage.Objects.Delete deleteRequest(StorageObject blob, Map
       public StorageObject compose(Iterable sources, StorageObject target,
           Map targetOptions) {
         ComposeRequest request = new ComposeRequest();
    -    if (target.getContentType() == null) {
    -      target.setContentType("application/octet-stream");
    -    }
         request.setDestination(target);
         List sourceObjects = new ArrayList<>();
         for (StorageObject source : sources) {
    @@ -584,7 +581,7 @@ private RewriteResponse rewrite(RewriteRequest req, String token) {
               ? req.megabytesRewrittenPerCall * MEGABYTE : null;
           com.google.api.services.storage.model.RewriteResponse rewriteResponse = storage.objects()
               .rewrite(req.source.getBucket(), req.source.getName(), req.target.getBucket(),
    -              req.target.getName(), req.target.getContentType() != null ? req.target : null)
    +              req.target.getName(), req.overrideInfo ? req.target : null)
               .setSourceGeneration(req.source.getGeneration())
               .setRewriteToken(token)
               .setMaxBytesRewrittenPerCall(maxBytesRewrittenPerCall)
    diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpc.java
    similarity index 95%
    rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java
    rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpc.java
    index d239a475a6dd..13dddb7d6737 100644
    --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpc.java
    +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpc.java
    @@ -14,15 +14,15 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.storage.spi;
    +package com.google.cloud.storage.spi;
     
     import static com.google.common.base.MoreObjects.firstNonNull;
     
     import com.google.api.services.storage.model.Bucket;
     import com.google.api.services.storage.model.StorageObject;
    +import com.google.cloud.storage.StorageException;
     import com.google.common.collect.ImmutableList;
     import com.google.common.collect.ImmutableMap;
    -import com.google.gcloud.storage.StorageException;
     
     import java.io.InputStream;
     import java.util.List;
    @@ -138,15 +138,17 @@ class RewriteRequest {
     
         public final StorageObject source;
         public final Map sourceOptions;
    +    public final boolean overrideInfo;
         public final StorageObject target;
         public final Map targetOptions;
         public final Long megabytesRewrittenPerCall;
     
         public RewriteRequest(StorageObject source, Map sourceOptions,
    -        StorageObject target, Map targetOptions,
    +        boolean overrideInfo, StorageObject target, Map targetOptions,
             Long megabytesRewrittenPerCall) {
           this.source = source;
           this.sourceOptions = sourceOptions;
    +      this.overrideInfo = overrideInfo;
           this.target = target;
           this.targetOptions = targetOptions;
           this.megabytesRewrittenPerCall = megabytesRewrittenPerCall;
    @@ -163,6 +165,7 @@ public boolean equals(Object obj) {
           final RewriteRequest other = (RewriteRequest) obj;
           return Objects.equals(this.source, other.source)
               && Objects.equals(this.sourceOptions, other.sourceOptions)
    +          && Objects.equals(this.overrideInfo, other.overrideInfo)
               && Objects.equals(this.target, other.target)
               && Objects.equals(this.targetOptions, other.targetOptions)
               && Objects.equals(this.megabytesRewrittenPerCall, other.megabytesRewrittenPerCall);
    @@ -170,7 +173,8 @@ public boolean equals(Object obj) {
     
         @Override
         public int hashCode() {
    -      return Objects.hash(source, sourceOptions, target, targetOptions, megabytesRewrittenPerCall);
    +      return Objects.hash(source, sourceOptions, overrideInfo, target, targetOptions,
    +          megabytesRewrittenPerCall);
         }
       }
     
    diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpcFactory.java
    similarity index 85%
    rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java
    rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpcFactory.java
    index 19b98e6273db..f765d3fea98b 100644
    --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/spi/StorageRpcFactory.java
    +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/spi/StorageRpcFactory.java
    @@ -14,10 +14,10 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.storage.spi;
    +package com.google.cloud.storage.spi;
     
    -import com.google.gcloud.spi.ServiceRpcFactory;
    -import com.google.gcloud.storage.StorageOptions;
    +import com.google.cloud.spi.ServiceRpcFactory;
    +import com.google.cloud.storage.StorageOptions;
     
     /**
      * An interface for Storage RPC factory.
    diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java
    similarity index 79%
    rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java
    rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java
    index 1287ede746d5..675c892fcc22 100644
    --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java
    +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/RemoteStorageHelper.java
    @@ -14,15 +14,15 @@
      * limitations under the License.
      */
     
    -package com.google.gcloud.storage.testing;
    +package com.google.cloud.storage.testing;
     
    -import com.google.gcloud.AuthCredentials;
    -import com.google.gcloud.RetryParams;
    -import com.google.gcloud.storage.BlobInfo;
    -import com.google.gcloud.storage.Storage;
    -import com.google.gcloud.storage.Storage.BlobListOption;
    -import com.google.gcloud.storage.StorageException;
    -import com.google.gcloud.storage.StorageOptions;
    +import com.google.cloud.AuthCredentials;
    +import com.google.cloud.RetryParams;
    +import com.google.cloud.storage.BlobInfo;
    +import com.google.cloud.storage.Storage;
    +import com.google.cloud.storage.Storage.BlobListOption;
    +import com.google.cloud.storage.StorageException;
    +import com.google.cloud.storage.StorageOptions;
     
     import java.io.IOException;
     import java.io.InputStream;
    @@ -47,13 +47,13 @@
      * {@link StorageOptions#connectTimeout()} and {@link StorageOptions#readTimeout()} are both set
      * to {@code 60000}.
      */
    -public class RemoteGcsHelper {
    +public class RemoteStorageHelper {
     
    -  private static final Logger log = Logger.getLogger(RemoteGcsHelper.class.getName());
    +  private static final Logger log = Logger.getLogger(RemoteStorageHelper.class.getName());
       private static final String BUCKET_NAME_PREFIX = "gcloud-test-bucket-temp-";
       private final StorageOptions options;
     
    -  private RemoteGcsHelper(StorageOptions options) {
    +  private RemoteStorageHelper(StorageOptions options) {
         this.options = options;
       }
     
    @@ -111,16 +111,17 @@ public static String generateBucketName() {
       }
     
       /**
    -   * Creates a {@code RemoteGcsHelper} object for the given project id and JSON key input stream.
    +   * Creates a {@code RemoteStorageHelper} object for the given project id and JSON key input
    +   * stream.
        *
        * @param projectId id of the project to be used for running the tests
        * @param keyStream input stream for a JSON key
    -   * @return A {@code RemoteGcsHelper} object for the provided options
    -   * @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if
    +   * @return A {@code RemoteStorageHelper} object for the provided options
    +   * @throws com.google.cloud.storage.testing.RemoteStorageHelper.StorageHelperException if
        *     {@code keyStream} is not a valid JSON key stream
        */
    -  public static RemoteGcsHelper create(String projectId, InputStream keyStream)
    -      throws GcsHelperException {
    +  public static RemoteStorageHelper create(String projectId, InputStream keyStream)
    +      throws StorageHelperException {
         try {
           StorageOptions storageOptions = StorageOptions.builder()
               .authCredentials(AuthCredentials.createForJson(keyStream))
    @@ -129,26 +130,26 @@ public static RemoteGcsHelper create(String projectId, InputStream keyStream)
               .connectTimeout(60000)
               .readTimeout(60000)
               .build();
    -      return new RemoteGcsHelper(storageOptions);
    +      return new RemoteStorageHelper(storageOptions);
         } catch (IOException ex) {
           if (log.isLoggable(Level.WARNING)) {
             log.log(Level.WARNING, ex.getMessage());
           }
    -      throw GcsHelperException.translate(ex);
    +      throw StorageHelperException.translate(ex);
         }
       }
     
       /**
    -   * Creates a {@code RemoteGcsHelper} object using default project id and authentication
    +   * Creates a {@code RemoteStorageHelper} object using default project id and authentication
        * credentials.
        */
    -  public static RemoteGcsHelper create() throws GcsHelperException {
    +  public static RemoteStorageHelper create() throws StorageHelperException {
         StorageOptions storageOptions = StorageOptions.builder()
             .retryParams(retryParams())
             .connectTimeout(60000)
             .readTimeout(60000)
             .build();
    -    return new RemoteGcsHelper(storageOptions);
    +    return new RemoteStorageHelper(storageOptions);
       }
     
       private static RetryParams retryParams() {
    @@ -196,20 +197,20 @@ public Boolean call() {
         }
       }
     
    -  public static class GcsHelperException extends RuntimeException {
    +  public static class StorageHelperException extends RuntimeException {
     
         private static final long serialVersionUID = -7756074894502258736L;
     
    -    public GcsHelperException(String message) {
    +    public StorageHelperException(String message) {
           super(message);
         }
     
    -    public GcsHelperException(String message, Throwable cause) {
    +    public StorageHelperException(String message, Throwable cause) {
           super(message, cause);
         }
     
    -    public static GcsHelperException translate(Exception ex) {
    -      return new GcsHelperException(ex.getMessage(), ex);
    +    public static StorageHelperException translate(Exception ex) {
    +      return new StorageHelperException(ex.getMessage(), ex);
         }
       }
     }
    diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/package-info.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java
    similarity index 85%
    rename from gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/package-info.java
    rename to gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java
    index 2ea1866f2966..3fbf95fb29f7 100644
    --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/package-info.java
    +++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/testing/package-info.java
    @@ -23,15 +23,15 @@
      *
      * 

    Before the test: *

     {@code
    - * RemoteGcsHelper gcsHelper = RemoteGcsHelper.create();
    - * Storage storage = gcsHelper.options().service();
    - * String bucket = RemoteGcsHelper.generateBucketName();
    + * RemoteStorageHelper helper = RemoteStorageHelper.create();
    + * Storage storage = helper.options().service();
    + * String bucket = RemoteStorageHelper.generateBucketName();
      * storage.create(BucketInfo.of(bucket));
      * } 
    * *

    After the test: *

     {@code
    - * RemoteGcsHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS);
    + * RemoteStorageHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS);
      * } 
    * * LocalGcsHelper helps with testing on an in-memory filesystem (this is best for unit tests). @@ -52,4 +52,4 @@ * @see * gcloud-java tools for testing */ -package com.google.gcloud.storage.testing; +package com.google.cloud.storage.testing; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/AclTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/AclTest.java similarity index 84% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/AclTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/AclTest.java index 1c62805b2a1b..26c37f53ba48 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/AclTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/AclTest.java @@ -14,21 +14,21 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.junit.Assert.assertEquals; import com.google.api.services.storage.model.BucketAccessControl; import com.google.api.services.storage.model.ObjectAccessControl; -import com.google.gcloud.storage.Acl.Domain; -import com.google.gcloud.storage.Acl.Entity; -import com.google.gcloud.storage.Acl.Entity.Type; -import com.google.gcloud.storage.Acl.Group; -import com.google.gcloud.storage.Acl.Project; -import com.google.gcloud.storage.Acl.Project.ProjectRole; -import com.google.gcloud.storage.Acl.RawEntity; -import com.google.gcloud.storage.Acl.Role; -import com.google.gcloud.storage.Acl.User; +import com.google.cloud.storage.Acl.Domain; +import com.google.cloud.storage.Acl.Entity; +import com.google.cloud.storage.Acl.Entity.Type; +import com.google.cloud.storage.Acl.Group; +import com.google.cloud.storage.Acl.Project; +import com.google.cloud.storage.Acl.Project.ProjectRole; +import com.google.cloud.storage.Acl.RawEntity; +import com.google.cloud.storage.Acl.Role; +import com.google.cloud.storage.Acl.User; import org.junit.Test; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchRequestTest.java similarity index 95% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchRequestTest.java index 63972ff85dfd..a47c7c140216 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchRequestTest.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Storage.PredefinedAcl.PUBLIC_READ; +import static com.google.cloud.storage.Storage.PredefinedAcl.PUBLIC_READ; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import com.google.cloud.storage.Storage.BlobGetOption; +import com.google.cloud.storage.Storage.BlobSourceOption; +import com.google.cloud.storage.Storage.BlobTargetOption; import com.google.common.collect.Iterables; -import com.google.gcloud.storage.Storage.BlobGetOption; -import com.google.gcloud.storage.Storage.BlobSourceOption; -import com.google.gcloud.storage.Storage.BlobTargetOption; import org.junit.Test; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchResponseTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchResponseTest.java similarity index 97% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchResponseTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchResponseTest.java index eb45b8b51271..a9177489c2fd 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchResponseTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BatchResponseTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import com.google.cloud.storage.BatchResponse.Result; import com.google.common.collect.ImmutableList; -import com.google.gcloud.storage.BatchResponse.Result; import org.easymock.EasyMock; import org.junit.Before; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobIdTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java similarity index 97% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobIdTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java index acc1885b9194..989d6ff82f40 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobIdTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobIdTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.junit.Assert.assertEquals; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java similarity index 93% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java index 029181c6c07b..03d28f8e988e 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobInfoTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobInfoTest.java @@ -14,21 +14,21 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Acl.Project.ProjectRole.VIEWERS; -import static com.google.gcloud.storage.Acl.Role.READER; -import static com.google.gcloud.storage.Acl.Role.WRITER; +import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS; +import static com.google.cloud.storage.Acl.Role.READER; +import static com.google.cloud.storage.Acl.Role.WRITER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import com.google.api.services.storage.model.StorageObject; +import com.google.cloud.storage.Acl.Project; +import com.google.cloud.storage.Acl.User; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.storage.Acl.Project; -import com.google.gcloud.storage.Acl.User; import org.junit.Test; @@ -51,7 +51,7 @@ public class BlobInfoTest { private static final Long DELETE_TIME = System.currentTimeMillis(); private static final String ETAG = "0xFF00"; private static final Long GENERATION = 1L; - private static final String ID = "B/N:1"; + private static final String GENERATED_ID = "B/N:1"; private static final String MD5 = "0xFF00"; private static final String MEDIA_LINK = "http://media/b/n"; private static final Map METADATA = ImmutableMap.of("n1", "v1", "n2", "v2"); @@ -71,7 +71,7 @@ public class BlobInfoTest { .crc32c(CRC32) .deleteTime(DELETE_TIME) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .md5(MD5) .mediaLink(MEDIA_LINK) .metadata(METADATA) @@ -118,7 +118,7 @@ public void testBuilder() { assertEquals(DELETE_TIME, BLOB_INFO.deleteTime()); assertEquals(ETAG, BLOB_INFO.etag()); assertEquals(GENERATION, BLOB_INFO.generation()); - assertEquals(ID, BLOB_INFO.id()); + assertEquals(GENERATED_ID, BLOB_INFO.generatedId()); assertEquals(MD5, BLOB_INFO.md5()); assertEquals(MEDIA_LINK, BLOB_INFO.mediaLink()); assertEquals(METADATA, BLOB_INFO.metadata()); @@ -141,7 +141,7 @@ public void testBuilder() { assertNull(DIRECTORY_INFO.deleteTime()); assertNull(DIRECTORY_INFO.etag()); assertNull(DIRECTORY_INFO.generation()); - assertNull(DIRECTORY_INFO.id()); + assertNull(DIRECTORY_INFO.generatedId()); assertNull(DIRECTORY_INFO.md5()); assertNull(DIRECTORY_INFO.mediaLink()); assertNull(DIRECTORY_INFO.metadata()); @@ -168,7 +168,7 @@ private void compareBlobs(BlobInfo expected, BlobInfo value) { assertEquals(expected.deleteTime(), value.deleteTime()); assertEquals(expected.etag(), value.etag()); assertEquals(expected.generation(), value.generation()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.md5(), value.md5()); assertEquals(expected.mediaLink(), value.mediaLink()); assertEquals(expected.metadata(), value.metadata()); @@ -203,7 +203,7 @@ public void testToPbAndFromPb() { assertNull(blobInfo.deleteTime()); assertNull(blobInfo.etag()); assertNull(blobInfo.generation()); - assertNull(blobInfo.id()); + assertNull(blobInfo.generatedId()); assertNull(blobInfo.md5()); assertNull(blobInfo.mediaLink()); assertNull(blobInfo.metadata()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobReadChannelTest.java similarity index 97% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobReadChannelTest.java index 1b0f36a864a2..a108e5f167ce 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobReadChannelTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobReadChannelTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; @@ -26,12 +26,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.ReadChannel; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryParams; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpcFactory; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryParams; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpcFactory; import org.junit.After; import org.junit.Before; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobTest.java similarity index 94% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobTest.java index 5a6173c08199..14cd6970fa87 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobTest.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Acl.Project.ProjectRole.VIEWERS; -import static com.google.gcloud.storage.Acl.Role.READER; -import static com.google.gcloud.storage.Acl.Role.WRITER; +import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS; +import static com.google.cloud.storage.Acl.Role.READER; +import static com.google.cloud.storage.Acl.Role.WRITER; import static org.easymock.EasyMock.capture; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -33,12 +33,12 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.ReadChannel; +import com.google.cloud.storage.Acl.Project; +import com.google.cloud.storage.Acl.User; +import com.google.cloud.storage.Storage.CopyRequest; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.storage.Acl.Project; -import com.google.gcloud.storage.Acl.User; -import com.google.gcloud.storage.Storage.CopyRequest; import org.easymock.Capture; import org.junit.After; @@ -64,7 +64,7 @@ public class BlobTest { private static final Long DELETE_TIME = System.currentTimeMillis(); private static final String ETAG = "0xFF00"; private static final Long GENERATION = 1L; - private static final String ID = "B/N:1"; + private static final String GENERATED_ID = "B/N:1"; private static final String MD5 = "0xFF00"; private static final String MEDIA_LINK = "http://media/b/n"; private static final Map METADATA = ImmutableMap.of("n1", "v1", "n2", "v2"); @@ -84,7 +84,7 @@ public class BlobTest { .crc32c(CRC32) .deleteTime(DELETE_TIME) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .md5(MD5) .mediaLink(MEDIA_LINK) .metadata(METADATA) @@ -233,6 +233,7 @@ public void testCopyToBucket() throws Exception { assertEquals(copyWriter, returnedCopyWriter); assertEquals(capturedCopyRequest.getValue().source(), blob.blobId()); assertEquals(capturedCopyRequest.getValue().target(), target); + assertFalse(capturedCopyRequest.getValue().overrideInfo()); assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty()); assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty()); } @@ -251,6 +252,7 @@ public void testCopyTo() throws Exception { assertEquals(copyWriter, returnedCopyWriter); assertEquals(capturedCopyRequest.getValue().source(), blob.blobId()); assertEquals(capturedCopyRequest.getValue().target(), target); + assertFalse(capturedCopyRequest.getValue().overrideInfo()); assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty()); assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty()); } @@ -258,9 +260,9 @@ public void testCopyTo() throws Exception { @Test public void testCopyToBlobId() throws Exception { initializeExpectedBlob(2); + BlobInfo target = BlobInfo.builder(BlobId.of("bt", "nt")).build(); BlobId targetId = BlobId.of("bt", "nt"); CopyWriter copyWriter = createMock(CopyWriter.class); - BlobInfo target = BlobInfo.builder(targetId).build(); Capture capturedCopyRequest = Capture.newInstance(); expect(storage.options()).andReturn(mockOptions); expect(storage.copy(capture(capturedCopyRequest))).andReturn(copyWriter); @@ -270,6 +272,7 @@ public void testCopyToBlobId() throws Exception { assertEquals(copyWriter, returnedCopyWriter); assertEquals(capturedCopyRequest.getValue().source(), blob.blobId()); assertEquals(capturedCopyRequest.getValue().target(), target); + assertFalse(capturedCopyRequest.getValue().overrideInfo()); assertTrue(capturedCopyRequest.getValue().sourceOptions().isEmpty()); assertTrue(capturedCopyRequest.getValue().targetOptions().isEmpty()); } @@ -335,7 +338,7 @@ public void testBuilder() { .crc32c(CRC32) .deleteTime(DELETE_TIME) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .md5(MD5) .mediaLink(MEDIA_LINK) .metadata(METADATA) @@ -357,7 +360,7 @@ public void testBuilder() { assertEquals(CRC32, blob.crc32c()); assertEquals(DELETE_TIME, blob.deleteTime()); assertEquals(ETAG, blob.etag()); - assertEquals(ID, blob.id()); + assertEquals(GENERATED_ID, blob.generatedId()); assertEquals(MD5, blob.md5()); assertEquals(MEDIA_LINK, blob.mediaLink()); assertEquals(METADATA, blob.metadata()); @@ -384,7 +387,7 @@ public void testBuilder() { assertNull(blob.crc32c()); assertNull(blob.deleteTime()); assertNull(blob.etag()); - assertNull(blob.id()); + assertNull(blob.generatedId()); assertNull(blob.md5()); assertNull(blob.mediaLink()); assertNull(blob.metadata()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobWriteChannelTest.java similarity index 97% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobWriteChannelTest.java index 18ec64a9575f..27d55d8b686c 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobWriteChannelTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BlobWriteChannelTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.capture; @@ -30,12 +30,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.RestorableState; +import com.google.cloud.RetryParams; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpcFactory; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryParams; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpcFactory; import org.easymock.Capture; import org.easymock.CaptureType; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java similarity index 85% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java index bd6bcdbbcff2..bcb4259ad564 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java @@ -14,24 +14,24 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Acl.Project.ProjectRole.VIEWERS; +import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.google.api.services.storage.model.Bucket.Lifecycle.Rule; +import com.google.cloud.storage.Acl.Project; +import com.google.cloud.storage.Acl.Role; +import com.google.cloud.storage.Acl.User; +import com.google.cloud.storage.BucketInfo.AgeDeleteRule; +import com.google.cloud.storage.BucketInfo.CreatedBeforeDeleteRule; +import com.google.cloud.storage.BucketInfo.DeleteRule; +import com.google.cloud.storage.BucketInfo.DeleteRule.Type; +import com.google.cloud.storage.BucketInfo.IsLiveDeleteRule; +import com.google.cloud.storage.BucketInfo.NumNewerVersionsDeleteRule; +import com.google.cloud.storage.BucketInfo.RawDeleteRule; import com.google.common.collect.ImmutableList; -import com.google.gcloud.storage.Acl.Project; -import com.google.gcloud.storage.Acl.Role; -import com.google.gcloud.storage.Acl.User; -import com.google.gcloud.storage.BucketInfo.AgeDeleteRule; -import com.google.gcloud.storage.BucketInfo.CreatedBeforeDeleteRule; -import com.google.gcloud.storage.BucketInfo.DeleteRule; -import com.google.gcloud.storage.BucketInfo.DeleteRule.Type; -import com.google.gcloud.storage.BucketInfo.IsLiveDeleteRule; -import com.google.gcloud.storage.BucketInfo.NumNewerVersionsDeleteRule; -import com.google.gcloud.storage.BucketInfo.RawDeleteRule; import org.junit.Test; @@ -44,7 +44,7 @@ public class BucketInfoTest { Acl.of(User.ofAllAuthenticatedUsers(), Role.READER), Acl.of(new Project(VIEWERS, "p1"), Role.WRITER)); private static final String ETAG = "0xFF00"; - private static final String ID = "B/N:1"; + private static final String GENERATED_ID = "B/N:1"; private static final Long META_GENERATION = 10L; private static final User OWNER = new User("user@gmail.com"); private static final String SELF_LINK = "http://storage/b/n"; @@ -62,7 +62,7 @@ public class BucketInfoTest { private static final BucketInfo BUCKET_INFO = BucketInfo.builder("b") .acl(ACL) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .metageneration(META_GENERATION) .owner(OWNER) .selfLink(SELF_LINK) @@ -80,10 +80,10 @@ public class BucketInfoTest { @Test public void testToBuilder() { compareBuckets(BUCKET_INFO, BUCKET_INFO.toBuilder().build()); - BucketInfo bucketInfo = BUCKET_INFO.toBuilder().name("B").id("id").build(); + BucketInfo bucketInfo = BUCKET_INFO.toBuilder().name("B").generatedId("id").build(); assertEquals("B", bucketInfo.name()); - assertEquals("id", bucketInfo.id()); - bucketInfo = bucketInfo.toBuilder().name("b").id(ID).build(); + assertEquals("id", bucketInfo.generatedId()); + bucketInfo = bucketInfo.toBuilder().name("b").generatedId(GENERATED_ID).build(); compareBuckets(BUCKET_INFO, bucketInfo); } @@ -104,7 +104,7 @@ public void testBuilder() { assertEquals("b", BUCKET_INFO.name()); assertEquals(ACL, BUCKET_INFO.acl()); assertEquals(ETAG, BUCKET_INFO.etag()); - assertEquals(ID, BUCKET_INFO.id()); + assertEquals(GENERATED_ID, BUCKET_INFO.generatedId()); assertEquals(META_GENERATION, BUCKET_INFO.metageneration()); assertEquals(OWNER, BUCKET_INFO.owner()); assertEquals(SELF_LINK, BUCKET_INFO.selfLink()); @@ -131,7 +131,7 @@ private void compareBuckets(BucketInfo expected, BucketInfo value) { assertEquals(expected.name(), value.name()); assertEquals(expected.acl(), value.acl()); assertEquals(expected.etag(), value.etag()); - assertEquals(expected.id(), value.id()); + assertEquals(expected.generatedId(), value.generatedId()); assertEquals(expected.metageneration(), value.metageneration()); assertEquals(expected.owner(), value.owner()); assertEquals(expected.selfLink(), value.selfLink()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketTest.java similarity index 95% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketTest.java index 236411e0c2d8..0494dfcdf506 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/BucketTest.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Acl.Project.ProjectRole.VIEWERS; -import static com.google.gcloud.storage.Acl.Role.READER; -import static com.google.gcloud.storage.Acl.Role.WRITER; +import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS; +import static com.google.cloud.storage.Acl.Role.READER; +import static com.google.cloud.storage.Acl.Role.WRITER; import static org.easymock.EasyMock.capture; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; @@ -30,14 +30,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import com.google.cloud.Page; +import com.google.cloud.PageImpl; +import com.google.cloud.storage.Acl.Project; +import com.google.cloud.storage.Acl.User; +import com.google.cloud.storage.BatchResponse.Result; +import com.google.cloud.storage.BucketInfo.AgeDeleteRule; +import com.google.cloud.storage.BucketInfo.DeleteRule; import com.google.common.collect.ImmutableList; -import com.google.gcloud.Page; -import com.google.gcloud.PageImpl; -import com.google.gcloud.storage.Acl.Project; -import com.google.gcloud.storage.Acl.User; -import com.google.gcloud.storage.BatchResponse.Result; -import com.google.gcloud.storage.BucketInfo.AgeDeleteRule; -import com.google.gcloud.storage.BucketInfo.DeleteRule; import org.easymock.Capture; import org.junit.After; @@ -59,7 +59,7 @@ public class BucketTest { private static final List ACL = ImmutableList.of( Acl.of(User.ofAllAuthenticatedUsers(), READER), Acl.of(new Project(VIEWERS, "p1"), WRITER)); private static final String ETAG = "0xFF00"; - private static final String ID = "B/N:1"; + private static final String GENERATED_ID = "B/N:1"; private static final Long META_GENERATION = 10L; private static final User OWNER = new User("user@gmail.com"); private static final String SELF_LINK = "http://storage/b/n"; @@ -77,7 +77,7 @@ public class BucketTest { private static final BucketInfo FULL_BUCKET_INFO = BucketInfo.builder("b") .acl(ACL) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .metageneration(META_GENERATION) .owner(OWNER) .selfLink(SELF_LINK) @@ -293,16 +293,16 @@ public void testCreate() throws Exception { } @Test - public void testCreateNullContentType() throws Exception { + public void testCreateNoContentType() throws Exception { initializeExpectedBucket(5); - BlobInfo info = BlobInfo.builder("b", "n").contentType(Storage.DEFAULT_CONTENT_TYPE).build(); + BlobInfo info = BlobInfo.builder("b", "n").build(); Blob expectedBlob = new Blob(serviceMockReturnsOptions, new BlobInfo.BuilderImpl(info)); byte[] content = {0xD, 0xE, 0xA, 0xD}; expect(storage.options()).andReturn(mockOptions); expect(storage.create(info, content)).andReturn(expectedBlob); replay(storage); initializeBucket(); - Blob blob = bucket.create("n", content, null); + Blob blob = bucket.create("n", content); assertEquals(expectedBlob, blob); } @@ -388,9 +388,9 @@ public void testCreateFromStream() throws Exception { } @Test - public void testCreateFromStreamNullContentType() throws Exception { + public void testCreateFromStreamNoContentType() throws Exception { initializeExpectedBucket(5); - BlobInfo info = BlobInfo.builder("b", "n").contentType(Storage.DEFAULT_CONTENT_TYPE).build(); + BlobInfo info = BlobInfo.builder("b", "n").build(); Blob expectedBlob = new Blob(serviceMockReturnsOptions, new BlobInfo.BuilderImpl(info)); byte[] content = {0xD, 0xE, 0xA, 0xD}; InputStream streamContent = new ByteArrayInputStream(content); @@ -398,7 +398,7 @@ public void testCreateFromStreamNullContentType() throws Exception { expect(storage.create(info, streamContent)).andReturn(expectedBlob); replay(storage); initializeBucket(); - Blob blob = bucket.create("n", streamContent, null); + Blob blob = bucket.create("n", streamContent); assertEquals(expectedBlob, blob); } @@ -495,7 +495,7 @@ public void testBuilder() { new Bucket.Builder(new Bucket(storage, new BucketInfo.BuilderImpl(BUCKET_INFO))); Bucket bucket = builder.acl(ACL) .etag(ETAG) - .id(ID) + .generatedId(GENERATED_ID) .metageneration(META_GENERATION) .owner(OWNER) .selfLink(SELF_LINK) @@ -512,7 +512,7 @@ public void testBuilder() { assertEquals("b", bucket.name()); assertEquals(ACL, bucket.acl()); assertEquals(ETAG, bucket.etag()); - assertEquals(ID, bucket.id()); + assertEquals(GENERATED_ID, bucket.generatedId()); assertEquals(META_GENERATION, bucket.metageneration()); assertEquals(OWNER, bucket.owner()); assertEquals(SELF_LINK, bucket.selfLink()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyRequestTest.java similarity index 80% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyRequestTest.java index b7e8d14e53a1..0649566864a1 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyRequestTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyRequestTest.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; -import static com.google.gcloud.storage.Storage.PredefinedAcl.PUBLIC_READ; +import static com.google.cloud.storage.Storage.PredefinedAcl.PUBLIC_READ; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import com.google.cloud.storage.Storage.BlobSourceOption; +import com.google.cloud.storage.Storage.BlobTargetOption; import com.google.common.collect.ImmutableList; -import com.google.gcloud.storage.Storage.BlobSourceOption; -import com.google.gcloud.storage.Storage.BlobTargetOption; import org.junit.Rule; import org.junit.Test; @@ -53,6 +55,7 @@ public void testCopyRequest() { assertEquals(1, copyRequest1.sourceOptions().size()); assertEquals(BlobSourceOption.generationMatch(1), copyRequest1.sourceOptions().get(0)); assertEquals(TARGET_BLOB_INFO, copyRequest1.target()); + assertTrue(copyRequest1.overrideInfo()); assertEquals(1, copyRequest1.targetOptions().size()); assertEquals(BlobTargetOption.predefinedAcl(PUBLIC_READ), copyRequest1.targetOptions().get(0)); @@ -62,6 +65,7 @@ public void testCopyRequest() { .build(); assertEquals(SOURCE_BLOB_ID, copyRequest2.source()); assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest2.target()); + assertFalse(copyRequest2.overrideInfo()); Storage.CopyRequest copyRequest3 = Storage.CopyRequest.builder() .source(SOURCE_BLOB_ID) @@ -69,6 +73,7 @@ public void testCopyRequest() { .build(); assertEquals(SOURCE_BLOB_ID, copyRequest3.source()); assertEquals(TARGET_BLOB_INFO, copyRequest3.target()); + assertTrue(copyRequest3.overrideInfo()); assertEquals(ImmutableList.of(BlobTargetOption.predefinedAcl(PUBLIC_READ)), copyRequest3.targetOptions()); } @@ -78,52 +83,36 @@ public void testCopyRequestOf() { Storage.CopyRequest copyRequest1 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_INFO); assertEquals(SOURCE_BLOB_ID, copyRequest1.source()); assertEquals(TARGET_BLOB_INFO, copyRequest1.target()); + assertTrue(copyRequest1.overrideInfo()); Storage.CopyRequest copyRequest2 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_NAME); assertEquals(SOURCE_BLOB_ID, copyRequest2.source()); - assertEquals(BlobInfo.builder(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME).build(), + assertEquals(BlobInfo.builder(BlobId.of(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME)).build(), copyRequest2.target()); + assertFalse(copyRequest2.overrideInfo()); Storage.CopyRequest copyRequest3 = Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_INFO); assertEquals(SOURCE_BLOB_ID, copyRequest3.source()); assertEquals(TARGET_BLOB_INFO, copyRequest3.target()); + assertTrue(copyRequest3.overrideInfo()); Storage.CopyRequest copyRequest4 = Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_NAME); assertEquals(SOURCE_BLOB_ID, copyRequest4.source()); - assertEquals(BlobInfo.builder(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME).build(), + assertEquals(BlobInfo.builder(BlobId.of(SOURCE_BUCKET_NAME, TARGET_BLOB_NAME)).build(), copyRequest4.target()); + assertFalse(copyRequest4.overrideInfo()); Storage.CopyRequest copyRequest5 = Storage.CopyRequest.of(SOURCE_BLOB_ID, TARGET_BLOB_ID); assertEquals(SOURCE_BLOB_ID, copyRequest5.source()); assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest5.target()); + assertFalse(copyRequest5.overrideInfo()); Storage.CopyRequest copyRequest6 = Storage.CopyRequest.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, TARGET_BLOB_ID); assertEquals(SOURCE_BLOB_ID, copyRequest6.source()); assertEquals(BlobInfo.builder(TARGET_BLOB_ID).build(), copyRequest6.target()); - } - - @Test - public void testCopyRequestFail() { - thrown.expect(IllegalArgumentException.class); - Storage.CopyRequest.builder() - .source(SOURCE_BLOB_ID) - .target(BlobInfo.builder(TARGET_BLOB_ID).build()) - .build(); - } - - @Test - public void testCopyRequestOfBlobInfoFail() { - thrown.expect(IllegalArgumentException.class); - Storage.CopyRequest.of(SOURCE_BLOB_ID, BlobInfo.builder(TARGET_BLOB_ID).build()); - } - - @Test - public void testCopyRequestOfStringFail() { - thrown.expect(IllegalArgumentException.class); - Storage.CopyRequest.of( - SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME, BlobInfo.builder(TARGET_BLOB_ID).build()); + assertFalse(copyRequest6.overrideInfo()); } } diff --git a/gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyWriterTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyWriterTest.java new file mode 100644 index 000000000000..5f5b9102c195 --- /dev/null +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CopyWriterTest.java @@ -0,0 +1,205 @@ +/* + * 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.cloud.storage; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.RestorableState; +import com.google.cloud.RetryParams; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.RewriteRequest; +import com.google.cloud.storage.spi.StorageRpc.RewriteResponse; +import com.google.cloud.storage.spi.StorageRpcFactory; +import com.google.common.collect.ImmutableMap; + +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +public class CopyWriterTest { + + private static final String SOURCE_BUCKET_NAME = "b"; + private static final String SOURCE_BLOB_NAME = "n"; + private static final String DESTINATION_BUCKET_NAME = "b1"; + private static final String DESTINATION_BLOB_NAME = "n1"; + private static final BlobId BLOB_ID = BlobId.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME); + private static final BlobInfo BLOB_INFO = + BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).build(); + private static final BlobInfo RESULT_INFO = + BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).contentType("type").build(); + private static final Map EMPTY_OPTIONS = ImmutableMap.of(); + private static final RewriteRequest REQUEST_WITH_OBJECT = + new StorageRpc.RewriteRequest(BLOB_ID.toPb(), EMPTY_OPTIONS, true, BLOB_INFO.toPb(), + EMPTY_OPTIONS, null); + private static final RewriteRequest REQUEST_WITHOUT_OBJECT = + new StorageRpc.RewriteRequest(BLOB_ID.toPb(), EMPTY_OPTIONS, false, BLOB_INFO.toPb(), + EMPTY_OPTIONS, null); + private static final RewriteResponse RESPONSE_WITH_OBJECT = new RewriteResponse( + REQUEST_WITH_OBJECT, null, 42L, false, "token", 21L); + private static final RewriteResponse RESPONSE_WITHOUT_OBJECT = new RewriteResponse( + REQUEST_WITHOUT_OBJECT, null, 42L, false, "token", 21L); + private static final RewriteResponse RESPONSE_WITH_OBJECT_DONE = + new RewriteResponse(REQUEST_WITH_OBJECT, RESULT_INFO.toPb(), 42L, true, "token", 42L); + private static final RewriteResponse RESPONSE_WITHOUT_OBJECT_DONE = + new RewriteResponse(REQUEST_WITHOUT_OBJECT, RESULT_INFO.toPb(), 42L, true, "token", 42L); + + private StorageOptions options; + private StorageRpcFactory rpcFactoryMock; + private StorageRpc storageRpcMock; + private CopyWriter copyWriter; + private Blob result; + + @Before + public void setUp() { + rpcFactoryMock = createMock(StorageRpcFactory.class); + storageRpcMock = createMock(StorageRpc.class); + expect(rpcFactoryMock.create(anyObject(StorageOptions.class))) + .andReturn(storageRpcMock); + replay(rpcFactoryMock); + options = StorageOptions.builder() + .projectId("projectid") + .serviceRpcFactory(rpcFactoryMock) + .retryParams(RetryParams.noRetries()) + .build(); + result = new Blob(options.service(), new BlobInfo.BuilderImpl(RESULT_INFO)); + } + + @After + public void tearDown() throws Exception { + verify(rpcFactoryMock, storageRpcMock); + } + + @Test + public void testRewriteWithObject() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT); + assertEquals(result, copyWriter.result()); + assertTrue(copyWriter.isDone()); + assertEquals(42L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + } + + @Test + public void testRewriteWithoutObject() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT)) + .andReturn(RESPONSE_WITHOUT_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT); + assertEquals(result, copyWriter.result()); + assertTrue(copyWriter.isDone()); + assertEquals(42L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + } + + @Test + public void testRewriteWithObjectMultipleRequests() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT); + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT); + assertEquals(result, copyWriter.result()); + assertTrue(copyWriter.isDone()); + assertEquals(42L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + } + + @Test + public void testRewriteWithoutObjectMultipleRequests() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT)) + .andReturn(RESPONSE_WITHOUT_OBJECT); + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT)) + .andReturn(RESPONSE_WITHOUT_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT); + assertEquals(result, copyWriter.result()); + assertTrue(copyWriter.isDone()); + assertEquals(42L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + } + + @Test + public void testSaveAndRestoreWithObject() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT); + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT); + copyWriter.copyChunk(); + assertTrue(!copyWriter.isDone()); + assertEquals(21L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + RestorableState rewriterState = copyWriter.capture(); + CopyWriter restoredRewriter = rewriterState.restore(); + assertEquals(result, restoredRewriter.result()); + assertTrue(restoredRewriter.isDone()); + assertEquals(42L, restoredRewriter.totalBytesCopied()); + assertEquals(42L, restoredRewriter.blobSize()); + } + + @Test + public void testSaveAndRestoreWithoutObject() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT)) + .andReturn(RESPONSE_WITHOUT_OBJECT); + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITHOUT_OBJECT)) + .andReturn(RESPONSE_WITHOUT_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITHOUT_OBJECT); + copyWriter.copyChunk(); + assertTrue(!copyWriter.isDone()); + assertEquals(21L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + RestorableState rewriterState = copyWriter.capture(); + CopyWriter restoredRewriter = rewriterState.restore(); + assertEquals(result, restoredRewriter.result()); + assertTrue(restoredRewriter.isDone()); + assertEquals(42L, restoredRewriter.totalBytesCopied()); + assertEquals(42L, restoredRewriter.blobSize()); + } + + @Test + public void testSaveAndRestoreWithResult() { + EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE_WITH_OBJECT)) + .andReturn(RESPONSE_WITH_OBJECT_DONE); + EasyMock.replay(storageRpcMock); + copyWriter = new CopyWriter(options, RESPONSE_WITH_OBJECT); + copyWriter.copyChunk(); + assertEquals(result, copyWriter.result()); + assertTrue(copyWriter.isDone()); + assertEquals(42L, copyWriter.totalBytesCopied()); + assertEquals(42L, copyWriter.blobSize()); + RestorableState rewriterState = copyWriter.capture(); + CopyWriter restoredRewriter = rewriterState.restore(); + assertEquals(result, restoredRewriter.result()); + assertTrue(restoredRewriter.isDone()); + assertEquals(42L, restoredRewriter.totalBytesCopied()); + assertEquals(42L, restoredRewriter.blobSize()); + } +} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CorsTest.java similarity index 95% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/CorsTest.java index f978cb87f3d1..b22547a34e54 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CorsTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/CorsTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.junit.Assert.assertEquals; +import com.google.cloud.storage.Cors.Origin; import com.google.common.collect.ImmutableList; -import com.google.gcloud.storage.Cors.Origin; import org.junit.Test; diff --git a/gcloud-java-storage/src/test/java/com/google/cloud/storage/OptionTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/OptionTest.java new file mode 100644 index 000000000000..6856745c0c4d --- /dev/null +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/OptionTest.java @@ -0,0 +1,65 @@ +/* + * 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.cloud.storage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import com.google.cloud.storage.spi.StorageRpc; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class OptionTest { + + private static final StorageRpc.Option RPC_OPTION = StorageRpc.Option.DELIMITER; + private static final StorageRpc.Option ANOTHER_RPC_OPTION = StorageRpc.Option.FIELDS; + private static final String VALUE = "some value"; + private static final String OTHER_VALUE = "another value"; + private static final Option OPTION = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_EQUALS = new Option(RPC_OPTION, VALUE) {}; + private static final Option OPTION_NOT_EQUALS1 = new Option(RPC_OPTION, OTHER_VALUE) {}; + private static final Option OPTION_NOT_EQUALS2 = new Option(ANOTHER_RPC_OPTION, VALUE) {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testEquals() { + assertEquals(OPTION, OPTION_EQUALS); + assertNotEquals(OPTION, OPTION_NOT_EQUALS1); + assertNotEquals(OPTION, OPTION_NOT_EQUALS2); + } + + @Test + public void testHashCode() { + assertEquals(OPTION.hashCode(), OPTION_EQUALS.hashCode()); + } + + @Test + public void testConstructor() { + assertEquals(RPC_OPTION, OPTION.rpcOption()); + assertEquals(VALUE, OPTION.value()); + Option option = new Option(RPC_OPTION, null) {}; + assertEquals(RPC_OPTION, option.rpcOption()); + assertNull(option.value()); + thrown.expect(NullPointerException.class); + new Option(null, VALUE) {}; + } +} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/SerializationTest.java similarity index 56% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/SerializationTest.java index ad13b14ae4e2..a8d399e1b1e4 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/SerializationTest.java @@ -14,33 +14,22 @@ * limitations under the License. */ -package com.google.gcloud.storage; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; +package com.google.cloud.storage; +import com.google.cloud.AuthCredentials; +import com.google.cloud.BaseSerializationTest; +import com.google.cloud.PageImpl; +import com.google.cloud.ReadChannel; +import com.google.cloud.Restorable; +import com.google.cloud.storage.Acl.Project.ProjectRole; +import com.google.cloud.storage.spi.StorageRpc; import com.google.common.collect.ImmutableMap; -import com.google.gcloud.AuthCredentials; -import com.google.gcloud.PageImpl; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryParams; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.Acl.Project.ProjectRole; -import com.google.gcloud.storage.spi.StorageRpc; - -import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.Map; -public class SerializationTest { +public class SerializationTest extends BaseSerializationTest { private static final Storage STORAGE = StorageOptions.builder().projectId("p").build().service(); private static final Acl.Domain ACL_DOMAIN = new Acl.Domain("domain"); @@ -63,8 +52,9 @@ public class SerializationTest { Collections.>emptyList()); private static final PageImpl PAGE_RESULT = new PageImpl<>(null, "c", Collections.singletonList(BLOB)); + private static final StorageException STORAGE_EXCEPTION = new StorageException(42, "message"); private static final Storage.BlobListOption BLOB_LIST_OPTIONS = - Storage.BlobListOption.maxResults(100); + Storage.BlobListOption.pageSize(100); private static final Storage.BlobSourceOption BLOB_SOURCE_OPTIONS = Storage.BlobSourceOption.generationMatch(1); private static final Storage.BlobTargetOption BLOB_TARGET_OPTIONS = @@ -77,82 +67,32 @@ public class SerializationTest { Storage.BucketTargetOption.metagenerationNotMatch(); private static final Map EMPTY_RPC_OPTIONS = ImmutableMap.of(); - @Test - public void testServiceOptions() throws Exception { + @Override + protected Serializable[] serializableObjects() { StorageOptions options = StorageOptions.builder() .projectId("p1") .authCredentials(AuthCredentials.createForAppEngine()) .build(); - StorageOptions serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - - options = options.toBuilder() + StorageOptions otherOptions = options.toBuilder() .projectId("p2") - .retryParams(RetryParams.defaultInstance()) .authCredentials(null) .build(); - serializedCopy = serializeAndDeserialize(options); - assertEquals(options, serializedCopy); - } - - @Test - public void testModelAndRequests() throws Exception { - Serializable[] objects = {ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, ACL, + return new Serializable[]{ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, ACL, BLOB_INFO, BLOB, BUCKET_INFO, BUCKET, ORIGIN, CORS, BATCH_REQUEST, BATCH_RESPONSE, PAGE_RESULT, BLOB_LIST_OPTIONS, BLOB_SOURCE_OPTIONS, BLOB_TARGET_OPTIONS, - BUCKET_LIST_OPTIONS, BUCKET_SOURCE_OPTIONS, BUCKET_TARGET_OPTIONS}; - for (Serializable obj : objects) { - Object copy = serializeAndDeserialize(obj); - assertEquals(obj, obj); - assertEquals(obj, copy); - assertNotSame(obj, copy); - assertEquals(copy, copy); - } + BUCKET_LIST_OPTIONS, BUCKET_SOURCE_OPTIONS, BUCKET_TARGET_OPTIONS, STORAGE_EXCEPTION, + options, otherOptions}; } - @Test - public void testReadChannelState() throws IOException, ClassNotFoundException { - StorageOptions options = StorageOptions.builder() - .projectId("p2") - .retryParams(RetryParams.defaultInstance()) - .build(); + @Override + protected Restorable[] restorableObjects() { + StorageOptions options = StorageOptions.builder().projectId("p2").build(); ReadChannel reader = new BlobReadChannel(options, BlobId.of("b", "n"), EMPTY_RPC_OPTIONS); - RestorableState state = reader.capture(); - RestorableState deserializedState = serializeAndDeserialize(state); - assertEquals(state, deserializedState); - assertEquals(state.hashCode(), deserializedState.hashCode()); - assertEquals(state.toString(), deserializedState.toString()); - reader.close(); - } - - @Test - public void testWriteChannelState() throws IOException, ClassNotFoundException { - StorageOptions options = StorageOptions.builder() - .projectId("p2") - .retryParams(RetryParams.defaultInstance()) - .build(); // avoid closing when you don't want partial writes to GCS upon failure @SuppressWarnings("resource") BlobWriteChannel writer = new BlobWriteChannel(options, BlobInfo.builder(BlobId.of("b", "n")).build(), "upload-id"); - RestorableState state = writer.capture(); - RestorableState deserializedState = serializeAndDeserialize(state); - assertEquals(state, deserializedState); - assertEquals(state.hashCode(), deserializedState.hashCode()); - assertEquals(state.toString(), deserializedState.toString()); - } - - @SuppressWarnings("unchecked") - private T serializeAndDeserialize(T obj) - throws IOException, ClassNotFoundException { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { - output.writeObject(obj); - } - try (ObjectInputStream input = - new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) { - return (T) input.readObject(); - } + return new Restorable[]{reader, writer}; } } diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageExceptionTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageExceptionTest.java similarity index 96% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageExceptionTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageExceptionTest.java index cf1d4b394e57..a562ec1194c9 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageExceptionTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageExceptionTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -26,8 +26,8 @@ import static org.junit.Assert.assertTrue; import com.google.api.client.googleapis.json.GoogleJsonError; -import com.google.gcloud.BaseServiceException; -import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.cloud.BaseServiceException; +import com.google.cloud.RetryHelper.RetryHelperException; import org.junit.Test; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java similarity index 95% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java index 9a306b2b03c6..47f776458876 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertArrayEquals; @@ -25,22 +25,22 @@ import static org.junit.Assert.assertTrue; import com.google.api.services.storage.model.StorageObject; +import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials; +import com.google.cloud.Page; +import com.google.cloud.ReadChannel; +import com.google.cloud.RetryParams; +import com.google.cloud.ServiceOptions; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.Storage.CopyRequest; +import com.google.cloud.storage.spi.StorageRpc; +import com.google.cloud.storage.spi.StorageRpc.Tuple; +import com.google.cloud.storage.spi.StorageRpcFactory; 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.common.io.BaseEncoding; -import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; -import com.google.gcloud.Page; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RetryParams; -import com.google.gcloud.ServiceOptions; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.Storage.CopyRequest; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.Tuple; -import com.google.gcloud.storage.spi.StorageRpcFactory; import org.easymock.Capture; import org.easymock.EasyMock; @@ -181,8 +181,8 @@ public class StorageImplTest { StorageRpc.Option.IF_SOURCE_GENERATION_MATCH, BLOB_SOURCE_GENERATION.value()); // Bucket list options - private static final Storage.BucketListOption BUCKET_LIST_MAX_RESULT = - Storage.BucketListOption.maxResults(42L); + private static final Storage.BucketListOption BUCKET_LIST_PAGE_SIZE = + Storage.BucketListOption.pageSize(42L); private static final Storage.BucketListOption BUCKET_LIST_PREFIX = Storage.BucketListOption.prefix("prefix"); private static final Storage.BucketListOption BUCKET_LIST_FIELDS = @@ -190,12 +190,12 @@ public class StorageImplTest { private static final Storage.BucketListOption BUCKET_LIST_EMPTY_FIELDS = Storage.BucketListOption.fields(); private static final Map BUCKET_LIST_OPTIONS = ImmutableMap.of( - StorageRpc.Option.MAX_RESULTS, BUCKET_LIST_MAX_RESULT.value(), + StorageRpc.Option.MAX_RESULTS, BUCKET_LIST_PAGE_SIZE.value(), StorageRpc.Option.PREFIX, BUCKET_LIST_PREFIX.value()); // Blob list options - private static final Storage.BlobListOption BLOB_LIST_MAX_RESULT = - Storage.BlobListOption.maxResults(42L); + private static final Storage.BlobListOption BLOB_LIST_PAGE_SIZE = + Storage.BlobListOption.pageSize(42L); private static final Storage.BlobListOption BLOB_LIST_PREFIX = Storage.BlobListOption.prefix("prefix"); private static final Storage.BlobListOption BLOB_LIST_FIELDS = @@ -205,7 +205,7 @@ public class StorageImplTest { private static final Storage.BlobListOption BLOB_LIST_EMPTY_FIELDS = Storage.BlobListOption.fields(); private static final Map BLOB_LIST_OPTIONS = ImmutableMap.of( - StorageRpc.Option.MAX_RESULTS, BLOB_LIST_MAX_RESULT.value(), + StorageRpc.Option.MAX_RESULTS, BLOB_LIST_PAGE_SIZE.value(), StorageRpc.Option.PREFIX, BLOB_LIST_PREFIX.value(), StorageRpc.Option.VERSIONS, BLOB_LIST_VERSIONS.value()); @@ -567,7 +567,7 @@ public void testListBucketsWithOptions() { EasyMock.replay(storageRpcMock); initializeService(); ImmutableList bucketList = ImmutableList.of(expectedBucket1, expectedBucket2); - Page page = storage.list(BUCKET_LIST_MAX_RESULT, BUCKET_LIST_PREFIX); + Page page = storage.list(BUCKET_LIST_PAGE_SIZE, BUCKET_LIST_PREFIX); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), Bucket.class)); } @@ -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)); @@ -654,7 +656,7 @@ public void testListBlobsWithOptions() { initializeService(); ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS); + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_VERSIONS); assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), Blob.class)); } @@ -673,19 +675,21 @@ public void testListBlobsWithSelectedFields() { initializeService(); ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS); - assertEquals(BLOB_LIST_MAX_RESULT.value(), - capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption())); + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS); + assertEquals(BLOB_LIST_PAGE_SIZE.value(), + capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.rpcOption())); 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)); } @@ -704,17 +708,19 @@ public void testListBlobsWithEmptyFields() { initializeService(); ImmutableList blobList = ImmutableList.of(expectedBlob1, expectedBlob2); Page page = - storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS); - assertEquals(BLOB_LIST_MAX_RESULT.value(), - capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption())); + storage.list(BUCKET_NAME1, BLOB_LIST_PAGE_SIZE, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS); + assertEquals(BLOB_LIST_PAGE_SIZE.value(), + capturedOptions.getValue().get(BLOB_LIST_PAGE_SIZE.rpcOption())); 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)); } @@ -866,7 +872,7 @@ public void testComposeWithOptions() { public void testCopy() { CopyRequest request = Storage.CopyRequest.of(BLOB_INFO1.blobId(), BLOB_INFO2.blobId()); StorageRpc.RewriteRequest rpcRequest = new StorageRpc.RewriteRequest(request.source().toPb(), - EMPTY_RPC_OPTIONS, request.target().toPb(), EMPTY_RPC_OPTIONS, null); + EMPTY_RPC_OPTIONS, false, BLOB_INFO2.toPb(), EMPTY_RPC_OPTIONS, null); StorageRpc.RewriteResponse rpcResponse = new StorageRpc.RewriteResponse(rpcRequest, null, 42L, false, "token", 21L); EasyMock.expect(storageRpcMock.openRewrite(rpcRequest)).andReturn(rpcResponse); @@ -886,7 +892,7 @@ public void testCopyWithOptions() { .target(BLOB_INFO1, BLOB_TARGET_GENERATION, BLOB_TARGET_METAGENERATION) .build(); StorageRpc.RewriteRequest rpcRequest = new StorageRpc.RewriteRequest(request.source().toPb(), - BLOB_SOURCE_OPTIONS_COPY, request.target().toPb(), BLOB_TARGET_OPTIONS_COMPOSE, null); + BLOB_SOURCE_OPTIONS_COPY, true, request.target().toPb(), BLOB_TARGET_OPTIONS_COMPOSE, null); StorageRpc.RewriteResponse rpcResponse = new StorageRpc.RewriteResponse(rpcRequest, null, 42L, false, "token", 21L); EasyMock.expect(storageRpcMock.openRewrite(rpcRequest)).andReturn(rpcResponse); @@ -906,7 +912,7 @@ public void testCopyWithOptionsFromBlobId() { .target(BLOB_INFO1, BLOB_TARGET_GENERATION, BLOB_TARGET_METAGENERATION) .build(); StorageRpc.RewriteRequest rpcRequest = new StorageRpc.RewriteRequest(request.source().toPb(), - BLOB_SOURCE_OPTIONS_COPY, request.target().toPb(), BLOB_TARGET_OPTIONS_COMPOSE, null); + BLOB_SOURCE_OPTIONS_COPY, true, request.target().toPb(), BLOB_TARGET_OPTIONS_COMPOSE, null); StorageRpc.RewriteResponse rpcResponse = new StorageRpc.RewriteResponse(rpcRequest, null, 42L, false, "token", 21L); EasyMock.expect(storageRpcMock.openRewrite(rpcRequest)).andReturn(rpcResponse); @@ -922,7 +928,7 @@ public void testCopyWithOptionsFromBlobId() { public void testCopyMultipleRequests() { CopyRequest request = Storage.CopyRequest.of(BLOB_INFO1.blobId(), BLOB_INFO2.blobId()); StorageRpc.RewriteRequest rpcRequest = new StorageRpc.RewriteRequest(request.source().toPb(), - EMPTY_RPC_OPTIONS, request.target().toPb(), EMPTY_RPC_OPTIONS, null); + EMPTY_RPC_OPTIONS, false, BLOB_INFO2.toPb(), EMPTY_RPC_OPTIONS, null); StorageRpc.RewriteResponse rpcResponse1 = new StorageRpc.RewriteResponse(rpcRequest, null, 42L, false, "token", 21L); StorageRpc.RewriteResponse rpcResponse2 = new StorageRpc.RewriteResponse(rpcRequest, @@ -935,7 +941,7 @@ public void testCopyMultipleRequests() { assertEquals(42L, writer.blobSize()); assertEquals(21L, writer.totalBytesCopied()); assertTrue(!writer.isDone()); - assertEquals(BLOB_INFO1, writer.result()); + assertEquals(expectedBlob1, writer.result()); assertTrue(writer.isDone()); assertEquals(42L, writer.totalBytesCopied()); assertEquals(42L, writer.blobSize()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/it/ITStorageTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java similarity index 92% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/it/ITStorageTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index 563a621c48fb..684653390d1b 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/it/ITStorageTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.gcloud.storage.it; +package com.google.cloud.storage.it; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertArrayEquals; @@ -25,29 +25,29 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.cloud.Page; +import com.google.cloud.ReadChannel; +import com.google.cloud.RestorableState; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.BatchRequest; +import com.google.cloud.storage.BatchResponse; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.HttpMethod; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.Storage.BlobField; +import com.google.cloud.storage.Storage.BucketField; +import com.google.cloud.storage.StorageException; +import com.google.cloud.storage.testing.RemoteStorageHelper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; -import com.google.gcloud.Page; -import com.google.gcloud.ReadChannel; -import com.google.gcloud.RestorableState; -import com.google.gcloud.WriteChannel; -import com.google.gcloud.storage.BatchRequest; -import com.google.gcloud.storage.BatchResponse; -import com.google.gcloud.storage.Blob; -import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.BlobInfo; -import com.google.gcloud.storage.Bucket; -import com.google.gcloud.storage.BucketInfo; -import com.google.gcloud.storage.CopyWriter; -import com.google.gcloud.storage.HttpMethod; -import com.google.gcloud.storage.Storage; -import com.google.gcloud.storage.Storage.BlobField; -import com.google.gcloud.storage.Storage.BucketField; -import com.google.gcloud.storage.StorageException; -import com.google.gcloud.storage.testing.RemoteGcsHelper; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -77,7 +77,7 @@ public class ITStorageTest { private static Storage storage; private static final Logger log = Logger.getLogger(ITStorageTest.class.getName()); - private static final String BUCKET = RemoteGcsHelper.generateBucketName(); + private static final String BUCKET = RemoteStorageHelper.generateBucketName(); private static final String CONTENT_TYPE = "text/plain"; private static final byte[] BLOB_BYTE_CONTENT = {0xD, 0xE, 0xA, 0xD}; private static final String BLOB_STRING_CONTENT = "Hello Google Cloud Storage!"; @@ -85,15 +85,15 @@ public class ITStorageTest { @BeforeClass public static void beforeClass() { - RemoteGcsHelper gcsHelper = RemoteGcsHelper.create(); - storage = gcsHelper.options().service(); + RemoteStorageHelper helper = RemoteStorageHelper.create(); + storage = helper.options().service(); storage.create(BucketInfo.of(BUCKET)); } @AfterClass public static void afterClass() throws ExecutionException, InterruptedException { if (storage != null) { - boolean wasDeleted = RemoteGcsHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS); + boolean wasDeleted = RemoteStorageHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS); if (!wasDeleted && log.isLoggable(Level.WARNING)) { log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); } @@ -122,7 +122,7 @@ public void testGetBucketSelectedFields() { Bucket remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID)); assertEquals(BUCKET, remoteBucket.name()); assertNull(remoteBucket.createTime()); - assertNotNull(remoteBucket.id()); + assertNotNull(remoteBucket.generatedId()); } @Test @@ -256,7 +256,7 @@ public void testGetBlobAllSelectedFields() { assertEquals(blob.bucket(), remoteBlob.bucket()); assertEquals(blob.name(), remoteBlob.name()); assertEquals(ImmutableMap.of("k", "v"), remoteBlob.metadata()); - assertNotNull(remoteBlob.id()); + assertNotNull(remoteBlob.generatedId()); assertNotNull(remoteBlob.selfLink()); assertTrue(remoteBlob.delete()); } @@ -368,7 +368,7 @@ public void testListBlobsEmptySelectedFields() throws InterruptedException { @Test(timeout = 15000) public void testListBlobsVersioned() throws ExecutionException, InterruptedException { - String bucketName = RemoteGcsHelper.generateBucketName(); + String bucketName = RemoteStorageHelper.generateBucketName(); Bucket bucket = storage.create(BucketInfo.builder(bucketName).versioningEnabled(true).build()); try { String[] blobNames = {"test-list-blobs-versioned-blob1", "test-list-blobs-versioned-blob2"}; @@ -407,7 +407,7 @@ public void testListBlobsVersioned() throws ExecutionException, InterruptedExcep assertTrue(remoteBlob2.delete()); assertTrue(remoteBlob3.delete()); } finally { - RemoteGcsHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS); + RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS); } } @@ -599,6 +599,37 @@ public void testComposeBlob() { assertNotNull(remoteTargetBlob); assertEquals(targetBlob.name(), remoteTargetBlob.name()); assertEquals(targetBlob.bucket(), remoteTargetBlob.bucket()); + assertNull(remoteTargetBlob.contentType()); + byte[] readBytes = storage.readAllBytes(BUCKET, targetBlobName); + byte[] composedBytes = Arrays.copyOf(BLOB_BYTE_CONTENT, BLOB_BYTE_CONTENT.length * 2); + System.arraycopy(BLOB_BYTE_CONTENT, 0, composedBytes, BLOB_BYTE_CONTENT.length, + BLOB_BYTE_CONTENT.length); + assertArrayEquals(composedBytes, readBytes); + assertTrue(remoteSourceBlob1.delete()); + assertTrue(remoteSourceBlob2.delete()); + assertTrue(remoteTargetBlob.delete()); + } + + @Test + public void testComposeBlobWithContentType() { + String sourceBlobName1 = "test-compose-blob-with-content-type-source-1"; + String sourceBlobName2 = "test-compose-blob-with-content-type-source-2"; + BlobInfo sourceBlob1 = BlobInfo.builder(BUCKET, sourceBlobName1).build(); + BlobInfo sourceBlob2 = BlobInfo.builder(BUCKET, sourceBlobName2).build(); + Blob remoteSourceBlob1 = storage.create(sourceBlob1, BLOB_BYTE_CONTENT); + Blob remoteSourceBlob2 = storage.create(sourceBlob2, BLOB_BYTE_CONTENT); + assertNotNull(remoteSourceBlob1); + assertNotNull(remoteSourceBlob2); + String targetBlobName = "test-compose-blob-with-content-type-target"; + BlobInfo targetBlob = + BlobInfo.builder(BUCKET, targetBlobName).contentType(CONTENT_TYPE).build(); + Storage.ComposeRequest req = + Storage.ComposeRequest.of(ImmutableList.of(sourceBlobName1, sourceBlobName2), targetBlob); + Blob remoteTargetBlob = storage.compose(req); + assertNotNull(remoteTargetBlob); + assertEquals(targetBlob.name(), remoteTargetBlob.name()); + assertEquals(targetBlob.bucket(), remoteTargetBlob.bucket()); + assertEquals(CONTENT_TYPE, remoteTargetBlob.contentType()); byte[] readBytes = storage.readAllBytes(BUCKET, targetBlobName); byte[] composedBytes = Arrays.copyOf(BLOB_BYTE_CONTENT, BLOB_BYTE_CONTENT.length * 2); System.arraycopy(BLOB_BYTE_CONTENT, 0, composedBytes, BLOB_BYTE_CONTENT.length, @@ -682,6 +713,26 @@ public void testCopyBlobUpdateMetadata() { assertTrue(storage.delete(BUCKET, targetBlobName)); } + @Test + public void testCopyBlobNoContentType() { + String sourceBlobName = "test-copy-blob-no-content-type-source"; + BlobId source = BlobId.of(BUCKET, sourceBlobName); + Blob remoteSourceBlob = storage.create(BlobInfo.builder(source).build(), BLOB_BYTE_CONTENT); + assertNotNull(remoteSourceBlob); + String targetBlobName = "test-copy-blob-no-content-type-target"; + ImmutableMap metadata = ImmutableMap.of("k", "v"); + BlobInfo target = BlobInfo.builder(BUCKET, targetBlobName).metadata(metadata).build(); + Storage.CopyRequest req = Storage.CopyRequest.of(source, target); + CopyWriter copyWriter = storage.copy(req); + assertEquals(BUCKET, copyWriter.result().bucket()); + assertEquals(targetBlobName, copyWriter.result().name()); + assertNull(copyWriter.result().contentType()); + assertEquals(metadata, copyWriter.result().metadata()); + assertTrue(copyWriter.isDone()); + assertTrue(remoteSourceBlob.delete()); + assertTrue(storage.delete(BUCKET, targetBlobName)); + } + @Test public void testCopyBlobFail() { String sourceBlobName = "test-copy-blob-source-fail"; diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java b/gcloud-java-storage/src/test/java/com/google/cloud/storage/testing/RemoteStorageHelperTest.java similarity index 69% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java rename to gcloud-java-storage/src/test/java/com/google/cloud/storage/testing/RemoteStorageHelperTest.java index 154554a029fe..3c29ca223c23 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java +++ b/gcloud-java-storage/src/test/java/com/google/cloud/storage/testing/RemoteStorageHelperTest.java @@ -14,15 +14,20 @@ * limitations under the License. */ -package com.google.gcloud.storage; +package com.google.cloud.storage.testing; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.google.cloud.Page; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.Storage.BlobListOption; +import com.google.cloud.storage.StorageException; +import com.google.cloud.storage.StorageOptions; import com.google.common.collect.ImmutableList; -import com.google.gcloud.Page; -import com.google.gcloud.storage.Storage.BlobListOption; -import com.google.gcloud.storage.testing.RemoteGcsHelper; import org.easymock.EasyMock; import org.junit.Before; @@ -37,7 +42,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -public class RemoteGcsHelperTest { +public class RemoteStorageHelperTest { private static final String BUCKET_NAME = "bucket-name"; private static final String PROJECT_ID = "project-id"; @@ -70,8 +75,11 @@ public class RemoteGcsHelperTest { private static final InputStream JSON_KEY_STREAM = new ByteArrayInputStream(JSON_KEY.getBytes()); private static final StorageException RETRYABLE_EXCEPTION = new StorageException(409, ""); private static final StorageException FATAL_EXCEPTION = new StorageException(500, ""); + private static final BlobId BLOB_ID1 = BlobId.of(BUCKET_NAME, "n1"); + private static final BlobId BLOB_ID2 = BlobId.of(BUCKET_NAME, "n2"); - private static Storage serviceMockReturnsOptions; + private Blob blob1; + private Blob blob2; private List blobList; private Page blobPage; @@ -80,18 +88,9 @@ public class RemoteGcsHelperTest { @Before public void setUp() { - serviceMockReturnsOptions = EasyMock.createMock(Storage.class); - EasyMock.expect(serviceMockReturnsOptions.options()) - .andReturn(EasyMock.createMock(StorageOptions.class)) - .times(2); - EasyMock.replay(serviceMockReturnsOptions); - blobList = ImmutableList.of( - new Blob( - serviceMockReturnsOptions, - new BlobInfo.BuilderImpl(BlobInfo.builder(BUCKET_NAME, "n1").build())), - new Blob( - serviceMockReturnsOptions, - new BlobInfo.BuilderImpl(BlobInfo.builder(BUCKET_NAME, "n2").build()))); + blob1 = EasyMock.createMock(Blob.class); + blob2 = EasyMock.createMock(Blob.class); + blobList = ImmutableList.of(blob1, blob2); blobPage = new Page() { @Override public String nextPageCursor() { @@ -118,43 +117,48 @@ public Iterator iterateAll() { @Test public void testForceDelete() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); + EasyMock.expect(blob1.blobId()).andReturn(BLOB_ID1); + EasyMock.expect(storageMock.delete(BLOB_ID1)).andReturn(true); + EasyMock.expect(blob2.blobId()).andReturn(BLOB_ID2); + EasyMock.expect(storageMock.delete(BLOB_ID2)).andReturn(true); EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true))) .andReturn(blobPage); - for (BlobInfo info : blobList) { - EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true); - } EasyMock.expect(storageMock.delete(BUCKET_NAME)).andReturn(true); - EasyMock.replay(storageMock); - assertTrue(RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS)); - EasyMock.verify(storageMock); + EasyMock.replay(storageMock, blob1, blob2); + assertTrue(RemoteStorageHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS)); + EasyMock.verify(storageMock, blob1, blob2); } @Test public void testForceDeleteTimeout() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); - EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(blobPage).anyTimes(); - for (BlobInfo info : blobList) { - EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true).anyTimes(); - } + EasyMock.expect(blob1.blobId()).andReturn(BLOB_ID1).anyTimes(); + EasyMock.expect(storageMock.delete(BLOB_ID1)).andReturn(true).anyTimes(); + EasyMock.expect(blob2.blobId()).andReturn(BLOB_ID2).anyTimes(); + EasyMock.expect(storageMock.delete(BLOB_ID2)).andReturn(true).anyTimes(); + EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true))) + .andReturn(blobPage).anyTimes(); EasyMock.expect(storageMock.delete(BUCKET_NAME)).andThrow(RETRYABLE_EXCEPTION).anyTimes(); - EasyMock.replay(storageMock); - assertTrue(!RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 50, TimeUnit.MICROSECONDS)); + EasyMock.replay(storageMock, blob1, blob2); + assertFalse( + RemoteStorageHelper.forceDelete(storageMock, BUCKET_NAME, 50, TimeUnit.MICROSECONDS)); EasyMock.verify(storageMock); } @Test public void testForceDeleteFail() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); + EasyMock.expect(blob1.blobId()).andReturn(BLOB_ID1); + EasyMock.expect(storageMock.delete(BLOB_ID1)).andReturn(true); + EasyMock.expect(blob2.blobId()).andReturn(BLOB_ID2); + EasyMock.expect(storageMock.delete(BLOB_ID2)).andReturn(true); EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true))) .andReturn(blobPage); - for (BlobInfo info : blobList) { - EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true); - } EasyMock.expect(storageMock.delete(BUCKET_NAME)).andThrow(FATAL_EXCEPTION); - EasyMock.replay(storageMock); + EasyMock.replay(storageMock, blob1, blob2); thrown.expect(ExecutionException.class); try { - RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS); + RemoteStorageHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS); } finally { EasyMock.verify(storageMock); } @@ -163,30 +167,32 @@ public void testForceDeleteFail() throws InterruptedException, ExecutionExceptio @Test public void testForceDeleteNoTimeout() { Storage storageMock = EasyMock.createMock(Storage.class); + EasyMock.expect(blob1.blobId()).andReturn(BLOB_ID1); + EasyMock.expect(storageMock.delete(BLOB_ID1)).andReturn(true); + EasyMock.expect(blob2.blobId()).andReturn(BLOB_ID2); + EasyMock.expect(storageMock.delete(BLOB_ID2)).andReturn(true); EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true))) .andReturn(blobPage); - for (BlobInfo info : blobList) { - EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true); - } EasyMock.expect(storageMock.delete(BUCKET_NAME)).andReturn(true); - EasyMock.replay(storageMock); - RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME); + EasyMock.replay(storageMock, blob1, blob2); + RemoteStorageHelper.forceDelete(storageMock, BUCKET_NAME); EasyMock.verify(storageMock); } @Test public void testForceDeleteNoTimeoutFail() { Storage storageMock = EasyMock.createMock(Storage.class); + EasyMock.expect(blob1.blobId()).andReturn(BLOB_ID1); + EasyMock.expect(storageMock.delete(BLOB_ID1)).andReturn(true); + EasyMock.expect(blob2.blobId()).andReturn(BLOB_ID2); + EasyMock.expect(storageMock.delete(BLOB_ID2)).andReturn(true); EasyMock.expect(storageMock.list(BUCKET_NAME, BlobListOption.versions(true))) .andReturn(blobPage); - for (BlobInfo info : blobList) { - EasyMock.expect(storageMock.delete(info.blobId())).andReturn(true); - } EasyMock.expect(storageMock.delete(BUCKET_NAME)).andThrow(FATAL_EXCEPTION); - EasyMock.replay(storageMock); + EasyMock.replay(storageMock, blob1, blob2); thrown.expect(StorageException.class); try { - RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME); + RemoteStorageHelper.forceDelete(storageMock, BUCKET_NAME); } finally { EasyMock.verify(storageMock); } @@ -194,7 +200,7 @@ public void testForceDeleteNoTimeoutFail() { @Test public void testCreateFromStream() { - RemoteGcsHelper helper = RemoteGcsHelper.create(PROJECT_ID, JSON_KEY_STREAM); + RemoteStorageHelper helper = RemoteStorageHelper.create(PROJECT_ID, JSON_KEY_STREAM); StorageOptions options = helper.options(); assertEquals(PROJECT_ID, options.projectId()); assertEquals(60000, options.connectTimeout()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java deleted file mode 100644 index ad4a04c34127..000000000000 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/CopyWriterTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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.storage; - -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableMap; -import com.google.gcloud.RestorableState; -import com.google.gcloud.RetryParams; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.spi.StorageRpc.RewriteRequest; -import com.google.gcloud.storage.spi.StorageRpc.RewriteResponse; -import com.google.gcloud.storage.spi.StorageRpcFactory; - -import org.easymock.EasyMock; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.Map; - -public class CopyWriterTest { - - private static final String SOURCE_BUCKET_NAME = "b"; - private static final String SOURCE_BLOB_NAME = "n"; - private static final String DESTINATION_BUCKET_NAME = "b1"; - private static final String DESTINATION_BLOB_NAME = "n1"; - private static final BlobId BLOB_ID = BlobId.of(SOURCE_BUCKET_NAME, SOURCE_BLOB_NAME); - private static final BlobInfo BLOB_INFO = - BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).build(); - private static final BlobInfo RESULT = - BlobInfo.builder(DESTINATION_BUCKET_NAME, DESTINATION_BLOB_NAME).contentType("type").build(); - private static final Map EMPTY_OPTIONS = ImmutableMap.of(); - private static final RewriteRequest REQUEST = new StorageRpc.RewriteRequest(BLOB_ID.toPb(), - EMPTY_OPTIONS, BLOB_INFO.toPb(), EMPTY_OPTIONS, null); - private static final RewriteResponse RESPONSE = new StorageRpc.RewriteResponse(REQUEST, - null, 42L, false, "token", 21L); - private static final RewriteResponse RESPONSE_DONE = new StorageRpc.RewriteResponse(REQUEST, - RESULT.toPb(), 42L, true, "token", 42L); - - private StorageOptions options; - private StorageRpcFactory rpcFactoryMock; - private StorageRpc storageRpcMock; - private CopyWriter copyWriter; - - @Before - public void setUp() { - rpcFactoryMock = createMock(StorageRpcFactory.class); - storageRpcMock = createMock(StorageRpc.class); - expect(rpcFactoryMock.create(anyObject(StorageOptions.class))) - .andReturn(storageRpcMock); - replay(rpcFactoryMock); - options = StorageOptions.builder() - .projectId("projectid") - .serviceRpcFactory(rpcFactoryMock) - .retryParams(RetryParams.noRetries()) - .build(); - } - - @After - public void tearDown() throws Exception { - verify(rpcFactoryMock, storageRpcMock); - } - - @Test - public void testRewrite() { - EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE); - EasyMock.replay(storageRpcMock); - copyWriter = new CopyWriter(options, RESPONSE); - assertEquals(RESULT, copyWriter.result()); - assertTrue(copyWriter.isDone()); - assertEquals(42L, copyWriter.totalBytesCopied()); - assertEquals(42L, copyWriter.blobSize()); - } - - @Test - public void testRewriteMultipleRequests() { - EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE); - EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE); - EasyMock.replay(storageRpcMock); - copyWriter = new CopyWriter(options, RESPONSE); - assertEquals(RESULT, copyWriter.result()); - assertTrue(copyWriter.isDone()); - assertEquals(42L, copyWriter.totalBytesCopied()); - assertEquals(42L, copyWriter.blobSize()); - } - - @Test - public void testSaveAndRestore() { - EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE); - EasyMock.expect(storageRpcMock.continueRewrite(RESPONSE)).andReturn(RESPONSE_DONE); - EasyMock.replay(storageRpcMock); - copyWriter = new CopyWriter(options, RESPONSE); - copyWriter.copyChunk(); - assertTrue(!copyWriter.isDone()); - assertEquals(21L, copyWriter.totalBytesCopied()); - assertEquals(42L, copyWriter.blobSize()); - RestorableState rewriterState = copyWriter.capture(); - CopyWriter restoredRewriter = rewriterState.restore(); - assertEquals(RESULT, restoredRewriter.result()); - assertTrue(restoredRewriter.isDone()); - assertEquals(42L, restoredRewriter.totalBytesCopied()); - assertEquals(42L, restoredRewriter.blobSize()); - } -} diff --git a/gcloud-java/README.md b/gcloud-java/README.md index e296d0c0c565..a01f794be966 100644 --- a/gcloud-java/README.md +++ b/gcloud-java/README.md @@ -5,7 +5,7 @@ Java idiomatic client for [Google Cloud Platform][cloud-platform] services. [![Build Status](https://travis-ci.org/GoogleCloudPlatform/gcloud-java.svg?branch=master)](https://travis-ci.org/GoogleCloudPlatform/gcloud-java) [![Coverage Status](https://coveralls.io/repos/GoogleCloudPlatform/gcloud-java/badge.svg?branch=master)](https://coveralls.io/r/GoogleCloudPlatform/gcloud-java?branch=master) -[![Maven](https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java.svg)]( https://img.shields.io/maven-central/v/com.google.gcloud/gcloud-java.svg) +[![Maven](https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java.svg)]( https://img.shields.io/maven-central/v/com.google.cloud/gcloud-java.svg) [![Codacy Badge](https://api.codacy.com/project/badge/grade/9da006ad7c3a4fe1abd142e77c003917)](https://www.codacy.com/app/mziccard/gcloud-java) [![Dependency Status](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969/badge.svg?style=flat)](https://www.versioneye.com/user/projects/56bd8ee72a29ed002d2b0969) @@ -25,18 +25,18 @@ Quickstart If you are using Maven, add this to your pom.xml file ```xml - com.google.gcloud + com.google.cloud gcloud-java - 0.1.5 + 0.2.0 ``` If you are using Gradle, add this to your dependencies ```Groovy -compile 'com.google.gcloud:gcloud-java:0.1.5' +compile 'com.google.cloud:gcloud-java:0.2.0' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.gcloud" % "gcloud-java" % "0.1.5" +libraryDependencies += "com.google.cloud" % "gcloud-java" % "0.2.0" ``` Troubleshooting @@ -80,7 +80,7 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-datastore]: https://cloud.google.com/datastore/docs [cloud-datastore-docs]: https://cloud.google.com/datastore/docs [cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate -[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/datastore/package-summary.html +[datastore-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/datastore/package-summary.html [cloud-pubsub]: https://cloud.google.com/pubsub/ [cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs @@ -88,4 +88,4 @@ Apache 2.0 - See [LICENSE] for more information. [cloud-storage]: https://cloud.google.com/storage/ [cloud-storage-docs]: https://cloud.google.com/storage/docs/overview [cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets -[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/gcloud/storage/package-summary.html +[storage-api]: http://googlecloudplatform.github.io/gcloud-java/apidocs/index.html?com/google/cloud/storage/package-summary.html diff --git a/gcloud-java/pom.xml b/gcloud-java/pom.xml index 19536faa8c8d..7dbd14e591cc 100644 --- a/gcloud-java/pom.xml +++ b/gcloud-java/pom.xml @@ -4,13 +4,14 @@ gcloud-java jar GCloud Java + https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java Java idiomatic client for Google Cloud Platform services. - com.google.gcloud + com.google.cloud gcloud-java-pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT @@ -28,6 +29,11 @@ gcloud-java-datastore ${project.version} + + ${project.groupId} + gcloud-java-dns + ${project.version} + ${project.groupId} gcloud-java-resourcemanager diff --git a/pom.xml b/pom.xml index b7766ac0e5c6..421ee0a49540 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,10 @@ 4.0.0 - com.google.gcloud + com.google.cloud gcloud-java-pom pom - 0.1.6-SNAPSHOT + 0.2.1-SNAPSHOT GCloud Java https://github.com/GoogleCloudPlatform/gcloud-java @@ -98,6 +98,7 @@ gcloud-java-contrib gcloud-java-core gcloud-java-datastore + gcloud-java-dns gcloud-java-examples gcloud-java-resourcemanager gcloud-java-storage @@ -215,6 +216,13 @@ + + + + test-jar + + + maven-compiler-plugin @@ -226,32 +234,6 @@ -Xlint:unchecked - - org.apache.maven.plugins - maven-source-plugin - 3.0.0 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - org.apache.maven.plugins maven-gpg-plugin @@ -301,10 +283,10 @@ true - com/google/gcloud/**/*.class + com/google/cloud/**/*.class - com/google/gcloud/examples/**/*.class + com/google/cloud/examples/**/*.class 256m @@ -392,19 +374,19 @@ API packages - com.google.gcloud* + com.google.cloud* Test helpers packages - com.google.gcloud.bigquery.testing:com.google.gcloud.datastore.testing:com.google.gcloud.resourcemanager.testing:com.google.gcloud.storage.testing + com.google.cloud.bigquery.testing:com.google.cloud.datastore.testing:com.google.cloud.dns.testing:com.google.cloud.resourcemanager.testing:com.google.cloud.storage.testing Example packages - com.google.gcloud.examples* + com.google.cloud.examples* SPI packages - com.google.gcloud.spi:com.google.gcloud.bigquery.spi:com.google.gcloud.datastore.spi:com.google.gcloud.resourcemanager.spi:com.google.gcloud.storage.spi + com.google.cloud.spi:com.google.cloud.bigquery.spi:com.google.cloud.datastore.spi:com.google.cloud.dns.spi:com.google.cloud.resourcemanager.spi:com.google.cloud.storage.spi @@ -436,4 +418,39 @@ + + + release + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + + + diff --git a/src/site/apt/gcloud-java-core/index.apt b/src/site/apt/gcloud-java-core/index.apt deleted file mode 100644 index 1dbbe002bd1b..000000000000 --- a/src/site/apt/gcloud-java-core/index.apt +++ /dev/null @@ -1,12 +0,0 @@ -GCloud Java Core: Common functionality for GCloud Java services. - - This module provides common functionality required by service-specific modules of this library. - This library is in a early stage of its development and may occasionally make backwards-incompatible changes, - but it is already usable. - -* Links - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-core}GitHub repository}} - - * {{{./apidocs/index.html?com/google/gcloud/package-summary.html}Javadocs}} - diff --git a/src/site/apt/gcloud-java-datastore/index.apt b/src/site/apt/gcloud-java-datastore/index.apt deleted file mode 100644 index 193fd958fda1..000000000000 --- a/src/site/apt/gcloud-java-datastore/index.apt +++ /dev/null @@ -1,16 +0,0 @@ -GCloud Java Datastore: Idiomatic Java Client for Google Cloud Datastore. - - This is a Java Client for accessing Google Cloud Datastore. - This library is in a early stage of its development and may occasionally make backwards-incompatible changes, - but it is already usable. - -* Features - - * {{{https://cloud.google.com/datastore/}Google Cloud Datastore}} - -* Links - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-datastore}GitHub repository}} - - * {{{./apidocs/index.html?com/google/gcloud/datastore/package-summary.html}Javadocs}} - diff --git a/src/site/apt/gcloud-java-examples/index.apt b/src/site/apt/gcloud-java-examples/index.apt deleted file mode 100644 index 349b06016d39..000000000000 --- a/src/site/apt/gcloud-java-examples/index.apt +++ /dev/null @@ -1,11 +0,0 @@ -GCloud Java Examples: Examples using GCloud Java services - - This library is in a early stage of its development and may occasionally make backwards-incompatible changes, - but it is already usable. - -* Links - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-examples}GitHub repository}} - - * {{{./apidocs/index.html?com/google/gcloud/examples/package-summary.html}Javadocs}} - diff --git a/src/site/apt/gcloud-java-storage/index.apt b/src/site/apt/gcloud-java-storage/index.apt deleted file mode 100644 index 5af2eb07133e..000000000000 --- a/src/site/apt/gcloud-java-storage/index.apt +++ /dev/null @@ -1,16 +0,0 @@ -GCloud Java Storage: Idiomatic Java Client for Google Cloud Storage. - - This is a Java Client for accessing Google Cloud Storage. - This library is in a early stage of its development and may occasionally make backwards-incompatible changes, - but it is already usable. - -* Features - - * {{{https://cloud.google.com/storage/}Google Cloud Storage}} - -* Links - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java/tree/master/gcloud-java-storage}GitHub repository}} - - * {{{./apidocs/index.html?com/google/gcloud/storage/package-summary.html}Javadocs}} - diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt deleted file mode 100644 index 395bfe7a65e0..000000000000 --- a/src/site/apt/index.apt +++ /dev/null @@ -1,21 +0,0 @@ -GCloud Java: Idiomatic Java Client for Google Cloud Platform services. - - This is a Java Client for accessing Google Cloud Platorm services such as Datastore, Storage, PubSub and others. - This library is in a early stage of its development and may occasionally make backwards-incompatible changes, - but it is already usable. - -* Features - - * {{{https://cloud.google.com/datastore/}Google Cloud Datastore}} - -* Links - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java}GitHub repository}} - - * {{{./apidocs/index.html}Javadocs}} - - * {{{https://travis-ci.org/GoogleCloudPlatform/gcloud-java} Continous Integration System (Travis-CI)}} - - * {{{https://github.com/GoogleCloudPlatform/gcloud-java/issues?page=1&state=open}Issues}} - - * {{{https://coveralls.io/r/GoogleCloudPlatform/gcloud-java/}Coverage}} diff --git a/src/site/resources/img/icon-lang-ruby.svg b/src/site/resources/img/icon-lang-ruby.svg index acfaab8d6ea5..5f4e5a25d893 100644 --- a/src/site/resources/img/icon-lang-ruby.svg +++ b/src/site/resources/img/icon-lang-ruby.svg @@ -1,23 +1,15 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/src/site/resources/index.html b/src/site/resources/index.html index 0e0933e7b68c..cba3e64a0962 100644 --- a/src/site/resources/index.html +++ b/src/site/resources/index.html @@ -60,10 +60,11 @@

    gcloud

    Quickstart with Maven: Add gcloud to your pom.xml

    <dependency>
    -  <groupId>com.google.gcloud</groupId>
    +  <groupId>com.google.cloud</groupId>
       <artifactId>gcloud-java</artifactId>
       <version>{{SITE_VERSION}}</version>
     </dependency>
    +

    As of April 12th, 2016, gcloud-java's group ID and base package were renamed to "com.google.cloud". If you haven't already, please update your dependencies.

    @@ -135,11 +136,11 @@

    Example: Retrieve Datastore Entries

    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
     
     // Authentication is automatic inside Google Compute Engine
     // and Google App Engine.
    @@ -152,12 +153,12 @@ 

    Example: Retrieve Datastore Entries

    -import com.google.gcloud.AuthCredentials;
    -import com.google.gcloud.datastore.Datastore;
    -import com.google.gcloud.datastore.DatastoreOptions;
    -import com.google.gcloud.datastore.Entity;
    -import com.google.gcloud.datastore.Key;
    -import com.google.gcloud.datastore.KeyFactory;
    +import com.google.cloud.AuthCredentials;
    +import com.google.cloud.datastore.Datastore;
    +import com.google.cloud.datastore.DatastoreOptions;
    +import com.google.cloud.datastore.Entity;
    +import com.google.cloud.datastore.Key;
    +import com.google.cloud.datastore.KeyFactory;
     
     DatastoreOptions options = DatastoreOptions.builder()
       .projectId(PROJECT_ID)
    @@ -178,10 +179,21 @@ 

    Examples

    • - SparkJava demo - Uses gcloud-java with App Engine Managed VMs, Datastore, and SparkJava. + Bookshelf - An App Engine app that manages a virtual bookshelf using gcloud-java libraries for Datastore and Storage. +
    • + +
    • + Flexible Environment/Datastore example - A simple app that uses Cloud Datastore to list the last 10 IP addresses that visited your site. Read about how to run the application here. +
    • +
    • + Flexible Environment/Storage example - An app that uploads files to a public Cloud Storage bucket on the App Engine Flexible Environment runtime. +
    • +
    • + SparkJava demo - Uses gcloud-java with App Engine Flexible Environment, Datastore, and SparkJava.
    • - Bookshelf - An App Engine app that manages a virtual bookshelf using gcloud-java libraries for Datastore and Storage. + TaskList - An command line app that manages a to-do list using Cloud Datastore. +
    diff --git a/utilities/after_success.sh b/utilities/after_success.sh index be7484806c46..7aeecd2f6bbd 100755 --- a/utilities/after_success.sh +++ b/utilities/after_success.sh @@ -12,8 +12,23 @@ if [ "${TRAVIS_JDK_VERSION}" == "oraclejdk7" ]; then if [ "${TRAVIS_PULL_REQUEST}" == "false" -a "${TRAVIS_BRANCH}" == "master" ]; then source ./utilities/integration_test_env.sh SITE_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -Ev '(^\[|\w+:)') + echo "Used the maven-help-plugin to determine that the version is $SITE_VERSION" + if [ "$SITE_VERSION" == "" ]; then + echo "Could not determine the version, so we're exiting." + exit 1 + fi if [ "${SITE_VERSION##*-}" != "SNAPSHOT" ]; then - # Deploy site if not a SNAPSHOT + # Deploy Maven artifacts (if they don't exist yet) and update artifact version in READMEs. + URL=https://oss.sonatype.org/content/repositories/releases/com/google/cloud/gcloud-java/$SITE_VERSION/ + if curl --output /dev/null --silent --head --fail "$URL"; then + echo "Not deploying artifacts because it seems like they already exist." + echo "Existence was checked using the url $URL" + else + mvn clean deploy -DskipITs --settings ~/.m2/settings.xml -P sign-deploy,release + fi + utilities/update_docs_version.sh + + # Create website git config --global user.name "travis-ci" git config --global user.email "travis@travis-ci.org" git clone --branch gh-pages --single-branch https://github.com/GoogleCloudPlatform/gcloud-java/ tmp_gh-pages @@ -28,15 +43,11 @@ if [ "${TRAVIS_JDK_VERSION}" == "oraclejdk7" ]; then git add index.html echo "" > apidocs/index.html git add apidocs/index.html - git commit -m "Added a new site for version $SITE_VERSION and updated the root directory's redirect." + git commit -m "Added a new site for version $SITE_VERSION and updated the root directory's redirect. [ci skip]" git config --global push.default simple git push --quiet "https://${CI_DEPLOY_USERNAME}:${CI_DEPLOY_PASSWORD}@github.com/GoogleCloudPlatform/gcloud-java.git" > /dev/null 2>&1 - - cd .. - utilities/update_docs_version.sh # Update version in READMEs - mvn clean deploy --settings ~/.m2/settings.xml -P sign-deploy else - mvn clean deploy -DskipTests=true -Dgpg.skip=true --settings ~/.m2/settings.xml + mvn clean deploy -DskipTests=true -Dgpg.skip=true --settings ~/.m2/settings.xml -P release fi else echo "Not deploying artifacts. This is only done with non-pull-request commits to master branch with Oracle Java 7 builds." diff --git a/utilities/update_docs_version.sh b/utilities/update_docs_version.sh index c8f132db2a3c..de53e0e282de 100755 --- a/utilities/update_docs_version.sh +++ b/utilities/update_docs_version.sh @@ -10,7 +10,7 @@ RELEASED_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate if [ "${RELEASED_VERSION##*-}" != "SNAPSHOT" ]; then echo "Changing version to $RELEASED_VERSION in README files" # Get list of directories for which README.md must be updated - module_folders=($(find . -maxdepth 1 -name 'gcloud-java*' -type d) .) + module_folders=($(find . -maxdepth 2 -type d | sed -E -n "/^\.\/(gcloud-java-contrib\/)?gcloud-java(-[a-z]+)+$/p") . ./gcloud-java) for item in ${module_folders[*]} do sed -ri "s/[0-9]+\.[0-9]+\.[0-9]+<\/version>/${RELEASED_VERSION}<\/version>/g" ${item}/README.md diff --git a/utilities/update_pom_version.sh b/utilities/update_pom_version.sh index d750ee4e9650..d4251bf053cf 100755 --- a/utilities/update_pom_version.sh +++ b/utilities/update_pom_version.sh @@ -11,8 +11,7 @@ # Get the previous maven project version. CURRENT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -Ev '(^\[|\w+:)') # Get list of directories for which pom.xml must be updated -module_folders=($(find . -maxdepth 1 -name 'gcloud-java*' -type d) .) - +module_folders=($(find . -maxdepth 2 -type d | sed -E -n "/^\.\/(gcloud-java-contrib\/)?gcloud-java(-[a-z]+)+$/p") . ./gcloud-java) if [ $# -eq 1 ]; then NEW_VERSION=$1 elif [ "${CURRENT_VERSION##*-}" != "SNAPSHOT" ]; then diff --git a/utilities/verify.sh b/utilities/verify.sh index b29ab8d8f747..26c490a55806 100755 --- a/utilities/verify.sh +++ b/utilities/verify.sh @@ -10,7 +10,7 @@ if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then chmod 700 $TRAVIS_BUILD_DIR/signing-tools tar xvf $TRAVIS_BUILD_DIR/signing-tools.tar -C $TRAVIS_BUILD_DIR/signing-tools # Run verify - mvn verify + mvn verify -P release else - mvn verify -DskipITs + mvn verify -DskipITs -P release fi