diff --git a/.github/scripts/update_generation_config.sh b/.github/scripts/update_generation_config.sh index 2acad04855fc..91434688cc5c 100644 --- a/.github/scripts/update_generation_config.sh +++ b/.github/scripts/update_generation_config.sh @@ -1,18 +1,50 @@ #!/bin/bash set -e # This script should be run at the root of the repository. -# This script is used to update googleapis commit to latest in generation -# configuration at the time of running and create a pull request. +# This script is used to update googleapis_commitish, gapic_generator_version, +# and libraries_bom_version in generation configuration at the time of running +# and create a pull request. # The following commands need to be installed before running the script: # 1. git # 2. gh +# 3. jq + +# Utility functions +# Get the latest released version of a Maven artifact. +function get_latest_released_version() { + local group_id=$1 + local artifact_id=$2 + latest=$(curl -s "https://search.maven.org/solrsearch/select?q=g:${group_id}+AND+a:${artifact_id}&core=gav&rows=500&wt=json" | jq -r '.response.docs[] | select(.v | test("^[0-9]+(\\.[0-9]+)*$")) | .v' | sort -V | tail -n 1) + echo "${latest}" +} + +# Update a key to a new value in the generation config. +function update_config() { + local key_word=$1 + local new_value=$2 + local file=$3 + echo "Update ${key_word} to ${new_value} in ${file}" + sed -i -e "s/^${key_word}.*$/${key_word}: ${new_value}/" "${file}" +} + +# Update an action to a new version in GitHub action. +function update_action() { + local key_word=$1 + local new_value=$2 + local file=$3 + echo "Update ${key_word} to ${new_value} in ${file}" + # use a different delimiter because the key_word contains "/". + sed -i -e "s|${key_word}@v.*$|${key_word}@v${new_value}|" "${file}" +} # The parameters of this script is: # 1. base_branch, the base branch of the result pull request. # 2. repo, organization/repo-name, e.g., googleapis/google-cloud-java # 3. [optional] generation_config, the path to the generation configuration, # the default value is generation_config.yaml in the repository root. +# 4. [optional] workflow, the library generation workflow file, +# the default value is .github/workflows/hermetic_library_generation.yaml. while [[ $# -gt 0 ]]; do key="$1" case "${key}" in @@ -28,6 +60,10 @@ case "${key}" in generation_config="$2" shift ;; + --workflow) + workflow="$2" + shift + ;; *) echo "Invalid option: [$1]" exit 1 @@ -51,8 +87,13 @@ if [ -z "${generation_config}" ]; then echo "Use default generation config: ${generation_config}" fi +if [ -z "${workflow}" ]; then + workflow=".github/workflows/hermetic_library_generation.yaml" + echo "Use default library generation workflow file: ${workflow}" +fi + current_branch="generate-libraries-${base_branch}" -title="chore: update googleapis commit at $(date)" +title="chore: Update generation configuration at $(date)" git checkout "${base_branch}" # Try to find a open pull request associated with the branch @@ -80,12 +121,25 @@ git pull latest_commit=$(git rev-parse HEAD) popd rm -rf tmp-googleapis -sed -i -e "s/^googleapis_commitish.*$/googleapis_commitish: ${latest_commit}/" "${generation_config}" +update_config "googleapis_commitish" "${latest_commit}" "${generation_config}" + +# Update gapic-generator-java version to the latest +latest_version=$(get_latest_released_version "com.google.api" "gapic-generator-java") +update_config "gapic_generator_version" "${latest_version}" "${generation_config}" + +# Update composite action version to latest gapic-generator-java version +update_action "googleapis/sdk-platform-java/.github/scripts" \ + "${latest_version}" \ + "${workflow}" + +# Update libraries-bom version to the latest +latest_version=$(get_latest_released_version "com.google.cloud" "libraries-bom") +update_config "libraries_bom_version" "${latest_version}" "${generation_config}" -git add "${generation_config}" +git add "${generation_config}" "${workflow}" changed_files=$(git diff --cached --name-only) if [[ "${changed_files}" == "" ]]; then - echo "The latest googleapis commit is not changed." + echo "The latest generation config is not changed." echo "Skip committing to the pull request." else git commit -m "${title}" @@ -110,4 +164,5 @@ if [ -z "${pr_num}" ]; then gh pr create --title "${title}" --head "${current_branch}" --body "${title}" --base "${base_branch}" else git push + gh pr edit "${pr_num}" --title "${title}" --body "${title}" fi diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml index 5ad2a254b92a..ca1baea12a6d 100644 --- a/.github/workflows/hermetic_library_generation.yaml +++ b/.github/workflows/hermetic_library_generation.yaml @@ -37,7 +37,7 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} - - uses: googleapis/sdk-platform-java/.github/scripts@v2.54.0 + - uses: googleapis/sdk-platform-java/.github/scripts@v2.55.1 if: env.SHOULD_RUN == 'true' with: base_ref: ${{ github.base_ref }} diff --git a/.github/workflows/update_generation_config.yaml b/.github/workflows/update_generation_config.yaml index 8cbe58ecab1f..01f3734c9a27 100644 --- a/.github/workflows/update_generation_config.yaml +++ b/.github/workflows/update_generation_config.yaml @@ -18,7 +18,6 @@ on: schedule: - cron: '0 2 * * *' workflow_dispatch: - jobs: update-generation-config: runs-on: ubuntu-22.04 @@ -28,15 +27,16 @@ jobs: steps: - uses: actions/checkout@v4 with: + fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} - name: Update params in generation config to latest shell: bash run: | - set -ex + set -x [ -z "$(git config user.email)" ] && git config --global user.email "cloud-java-bot@google.com" [ -z "$(git config user.name)" ] && git config --global user.name "cloud-java-bot" bash .github/scripts/update_generation_config.sh \ - --base_branch "${base_branch}"\ + --base_branch "${base_branch}" \ --repo ${{ github.repository }} env: GH_TOKEN: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b8785875668..39e42fb3d54b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [2.56.0](https://github.com/googleapis/java-bigtable/compare/v2.55.0...v2.56.0) (2025-03-18) + + +### Features + +* **bigtable:** Add support for Logical Views in Admin API ([#2519](https://github.com/googleapis/java-bigtable/issues/2519)) ([6dac3fd](https://github.com/googleapis/java-bigtable/commit/6dac3fd6443e94674af88a1dc97bedd9b3b0c834)) +* **bigtable:** Add support for Materialized Views in Admin API ([#2511](https://github.com/googleapis/java-bigtable/issues/2511)) ([55cd719](https://github.com/googleapis/java-bigtable/commit/55cd719df277a2ae1988c7cd53286558ad86835b)) + + +### Bug Fixes + +* **deps:** Update the Java code generator (gapic-generator-java) to 2.55.1 ([7992af0](https://github.com/googleapis/java-bigtable/commit/7992af08b4eb2f408ecb739a73fbdc36ca7af2b5)) + + +### Dependencies + +* Sdk-platform-java-config 3.45.1 ([#2517](https://github.com/googleapis/java-bigtable/issues/2517)) ([b2af258](https://github.com/googleapis/java-bigtable/commit/b2af258ed72d29644c8bd1079b1d0f223206d75b)) + ## [2.55.0](https://github.com/googleapis/java-bigtable/compare/v2.54.0...v2.55.0) (2025-03-11) diff --git a/README.md b/README.md index fbc5d626b03d..861ee694d10c 100644 --- a/README.md +++ b/README.md @@ -49,20 +49,20 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.51.0') +implementation platform('com.google.cloud:libraries-bom:26.56.0') implementation 'com.google.cloud:google-cloud-bigtable' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-bigtable:2.55.0' +implementation 'com.google.cloud:google-cloud-bigtable:2.56.0' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.55.0" +libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.56.0" ``` ## Authentication @@ -543,7 +543,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-bigtable/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-bigtable.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.55.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-bigtable/2.56.0 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/generation_config.yaml b/generation_config.yaml index 6833a41c8ec3..f53837082888 100644 --- a/generation_config.yaml +++ b/generation_config.yaml @@ -1,6 +1,6 @@ -gapic_generator_version: 2.54.0 -googleapis_commitish: 66ab6ceb69178f5368b130949eaf518b7c28352f -libraries_bom_version: 26.51.0 +gapic_generator_version: 2.55.1 +googleapis_commitish: 9ec67d424cd823b06c1b974d67df9ecb6e36edcd +libraries_bom_version: 26.56.0 template_excludes: - .gitignore - .kokoro/presubmit/integration.cfg diff --git a/google-cloud-bigtable-bom/pom.xml b/google-cloud-bigtable-bom/pom.xml index 7e21606eb5db..ca926cedb71f 100644 --- a/google-cloud-bigtable-bom/pom.xml +++ b/google-cloud-bigtable-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom com.google.cloud @@ -63,37 +63,37 @@ com.google.cloud google-cloud-bigtable - 2.55.0 + 2.56.0 com.google.cloud google-cloud-bigtable-emulator - 0.192.0 + 0.193.0 com.google.cloud google-cloud-bigtable-emulator-core - 0.192.0 + 0.193.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 diff --git a/google-cloud-bigtable-deps-bom/pom.xml b/google-cloud-bigtable-deps-bom/pom.xml index f1816f15f450..ef16b5597646 100644 --- a/google-cloud-bigtable-deps-bom/pom.xml +++ b/google-cloud-bigtable-deps-bom/pom.xml @@ -13,7 +13,7 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom diff --git a/google-cloud-bigtable-emulator-core/pom.xml b/google-cloud-bigtable-emulator-core/pom.xml index cf7c8815c63c..025cb888804c 100644 --- a/google-cloud-bigtable-emulator-core/pom.xml +++ b/google-cloud-bigtable-emulator-core/pom.xml @@ -7,11 +7,11 @@ google-cloud-bigtable-parent com.google.cloud - 2.55.0 + 2.56.0 google-cloud-bigtable-emulator-core - 0.192.0 + 0.193.0 A Java wrapper for the Cloud Bigtable emulator. diff --git a/google-cloud-bigtable-emulator/pom.xml b/google-cloud-bigtable-emulator/pom.xml index 5cb7b29d41aa..8695a22a7f25 100644 --- a/google-cloud-bigtable-emulator/pom.xml +++ b/google-cloud-bigtable-emulator/pom.xml @@ -5,7 +5,7 @@ 4.0.0 google-cloud-bigtable-emulator - 0.192.0 + 0.193.0 Google Cloud Java - Bigtable Emulator https://github.com/googleapis/java-bigtable @@ -14,7 +14,7 @@ com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 scm:git:git@github.com:googleapis/java-bigtable.git @@ -81,14 +81,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import @@ -99,7 +99,7 @@ com.google.cloud google-cloud-bigtable-emulator-core - 0.192.0 + 0.193.0 diff --git a/google-cloud-bigtable/clirr-ignored-differences.xml b/google-cloud-bigtable/clirr-ignored-differences.xml index 83a97c27e8c9..303ecc3a06ba 100644 --- a/google-cloud-bigtable/clirr-ignored-differences.xml +++ b/google-cloud-bigtable/clirr-ignored-differences.xml @@ -314,4 +314,10 @@ com/google/cloud/bigtable/data/v2/models/TargetId *scopedForMaterializedView() + + + 7009 + com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter + * + diff --git a/google-cloud-bigtable/pom.xml b/google-cloud-bigtable/pom.xml index ad6ffa1636ee..2fff6bbb3bf4 100644 --- a/google-cloud-bigtable/pom.xml +++ b/google-cloud-bigtable/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-bigtable - 2.55.0 + 2.56.0 jar Google Cloud Bigtable https://github.com/googleapis/java-bigtable @@ -12,11 +12,11 @@ com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 - 2.55.0 + 2.56.0 google-cloud-bigtable @@ -52,14 +52,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java index d2b21b8ba299..0985d2a6ac99 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/Version.java @@ -20,6 +20,6 @@ @InternalApi("For internal use only") public final class Version { // {x-version-update-start:google-cloud-bigtable:current} - public static String VERSION = "2.55.0"; + public static String VERSION = "2.56.0"; // {x-version-update-end} } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java index 793766c26a19..94e9ed7ddf68 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java @@ -22,13 +22,23 @@ import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.NotFoundException; import com.google.bigtable.admin.v2.DeleteAppProfileRequest; +import com.google.bigtable.admin.v2.DeleteLogicalViewRequest; +import com.google.bigtable.admin.v2.DeleteMaterializedViewRequest; import com.google.bigtable.admin.v2.GetAppProfileRequest; +import com.google.bigtable.admin.v2.GetLogicalViewRequest; +import com.google.bigtable.admin.v2.GetMaterializedViewRequest; import com.google.bigtable.admin.v2.ListAppProfilesRequest; +import com.google.bigtable.admin.v2.ListLogicalViewsRequest; +import com.google.bigtable.admin.v2.ListMaterializedViewsRequest; import com.google.bigtable.admin.v2.PartialUpdateClusterRequest; import com.google.cloud.Policy; import com.google.cloud.Policy.DefaultMarshaller; import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPage; import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPagedResponse; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListLogicalViewsPage; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListLogicalViewsPagedResponse; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListMaterializedViewsPage; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListMaterializedViewsPagedResponse; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.cloud.bigtable.admin.v2.models.AppProfile; import com.google.cloud.bigtable.admin.v2.models.Cluster; @@ -36,11 +46,17 @@ import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateLogicalViewRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateMaterializedViewRequest; import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.LogicalView; +import com.google.cloud.bigtable.admin.v2.models.MaterializedView; import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.models.PartialListInstancesException; import com.google.cloud.bigtable.admin.v2.models.UpdateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest; +import com.google.cloud.bigtable.admin.v2.models.UpdateLogicalViewRequest; +import com.google.cloud.bigtable.admin.v2.models.UpdateMaterializedViewRequest; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; @@ -1392,6 +1408,594 @@ public List apply(TestIamPermissionsResponse input) { MoreExecutors.directExecutor()); } + /** + * Creates a new materialized view. + * + *

Sample code: + * + *

{@code
+   * MaterializedView materializedView = client.createMaterializedView(
+   *   CreateMaterializedViewRequest.of("my-instance", "my-new-materialized-view")
+   *     .setQuery(query)
+   * );
+   * }
+ * + * @see CreateMaterializedViewRequest + */ + @SuppressWarnings("WeakerAccess") + public MaterializedView createMaterializedView(CreateMaterializedViewRequest request) { + return ApiExceptions.callAndTranslateApiException(createMaterializedViewAsync(request)); + } + + /** + * Asynchronously creates a new materialized view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture materializedViewFuture = client.createMaterializedViewAsync(
+   *   CreateMaterializedViewRequest.of("my-instance", "my-new-materialized-view")
+   *     .setQuery(query)
+   * );
+   *
+   * MaterializedView materializedView = materializedViewFuture.get();
+   * }
+ * + * @see CreateMaterializedViewRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture createMaterializedViewAsync( + CreateMaterializedViewRequest request) { + return ApiFutures.transform( + stub.createMaterializedViewOperationCallable().futureCall(request.toProto(projectId)), + new ApiFunction() { + @Override + public MaterializedView apply(com.google.bigtable.admin.v2.MaterializedView proto) { + return MaterializedView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Gets the materialized view by ID. + * + *

Sample code: + * + *

{@code
+   * MaterializedView materializedView = client.getMaterializedView("my-instance", "my-materialized-view");
+   * }
+ * + * @see MaterializedView + */ + public MaterializedView getMaterializedView(String instanceId, String materializedViewId) { + return ApiExceptions.callAndTranslateApiException( + getMaterializedViewAsync(instanceId, materializedViewId)); + } + + /** + * Asynchronously gets the materialized view by ID. + * + *

Sample code: + * + *

{@code
+   * ApiFuture materializedViewFuture = client.getMaterializedViewAsync("my-instance", "my-materialized-view");
+   *
+   * MaterializedView materializedView = materializedViewFuture.get();
+   * }
+ * + * @see MaterializedView + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture getMaterializedViewAsync( + String instanceId, String materializedViewId) { + String name = NameUtil.formatMaterializedViewName(projectId, instanceId, materializedViewId); + + GetMaterializedViewRequest request = + GetMaterializedViewRequest.newBuilder().setName(name).build(); + + return ApiFutures.transform( + stub.getMaterializedViewCallable().futureCall(request), + new ApiFunction() { + @Override + public MaterializedView apply(com.google.bigtable.admin.v2.MaterializedView proto) { + return MaterializedView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Lists all materialized views of the specified instance. + * + *

Sample code: + * + *

{@code
+   * List materializedViews = client.listMaterializedViews("my-instance");
+   * }
+ * + * @see MaterializedView + */ + @SuppressWarnings("WeakerAccess") + public List listMaterializedViews(String instanceId) { + return ApiExceptions.callAndTranslateApiException(listMaterializedViewsAsync(instanceId)); + } + + /** + * Asynchronously lists all materialized views of the specified instance. + * + *

Sample code: + * + *

{@code
+   * ApiFuture> materializedViewsFuture = client.listMaterializedViewsAsync("my-instance");
+   *
+   * List materializedViews = materializedViewFuture.get();
+   * }
+ * + * @see MaterializedView + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture> listMaterializedViewsAsync(String instanceId) { + String instanceName = NameUtil.formatInstanceName(projectId, instanceId); + + ListMaterializedViewsRequest request = + ListMaterializedViewsRequest.newBuilder().setParent(instanceName).build(); + + // TODO(igorbernstein2): try to upstream pagination spooling or figure out a way to expose the + // paginated responses while maintaining the wrapper facade. + + // Fetches the first page. + ApiFuture firstPageFuture = + ApiFutures.transform( + stub.listMaterializedViewsPagedCallable().futureCall(request), + new ApiFunction() { + @Override + public ListMaterializedViewsPage apply(ListMaterializedViewsPagedResponse response) { + return response.getPage(); + } + }, + MoreExecutors.directExecutor()); + + // Fetches the rest of the pages by chaining the futures. + ApiFuture> allProtos = + ApiFutures.transformAsync( + firstPageFuture, + new ApiAsyncFunction< + ListMaterializedViewsPage, List>() { + List responseAccumulator = + Lists.newArrayList(); + + @Override + public ApiFuture> apply( + ListMaterializedViewsPage page) { + // Add all entries from the page + responseAccumulator.addAll(Lists.newArrayList(page.getValues())); + + // If this is the last page, just return the accumulated responses. + if (!page.hasNextPage()) { + return ApiFutures.immediateFuture(responseAccumulator); + } + + // Otherwise fetch the next page. + return ApiFutures.transformAsync( + page.getNextPageAsync(), this, MoreExecutors.directExecutor()); + } + }, + MoreExecutors.directExecutor()); + + // Wraps all of the accumulated protos. + return ApiFutures.transform( + allProtos, + new ApiFunction< + List, List>() { + @Override + public List apply( + List input) { + List results = Lists.newArrayListWithCapacity(input.size()); + for (com.google.bigtable.admin.v2.MaterializedView materializedView : input) { + results.add(MaterializedView.fromProto(materializedView)); + } + return results; + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Updates an existing materialized view. + * + *

Sample code: + * + *

{@code
+   * MaterializedView existingMaterializedView = client.getMaterializedView("my-instance", "my-materialized-view");
+   *
+   * MaterializedView updatedMaterializedView = client.updateMaterializedView(
+   *   UpdateMaterializedViewRequest.of(existingMaterializedView)
+   *     .setDeletionProtection(false)
+   * );
+   * }
+ * + * @see UpdateMaterializedViewRequest + */ + @SuppressWarnings("WeakerAccess") + public MaterializedView updateMaterializedView(UpdateMaterializedViewRequest request) { + return ApiExceptions.callAndTranslateApiException(updateMaterializedViewAsync(request)); + } + + /** + * Asynchronously updates an existing materialized view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture existingMaterializedViewFuture = client.getMaterializedViewAsync("my-instance", "my-materialized-view");
+   *
+   * ApiFuture updatedMaterializedViewFuture = ApiFutures.transformAsync(
+   *   existingMaterializedViewFuture,
+   *   new ApiAsyncFunction() {
+   *     public ApiFuture apply(MaterializedView existingMaterializedView) {
+   *       return client.updateMaterializedViewAsync(
+   *         UpdateMaterializedViewRequest.of(existingMaterializedView)
+   *           .setDeletionProtection(false)
+   *       );
+   *     }
+   *   },
+   *   MoreExecutors.directExecutor()
+   * );
+   *
+   * ApiFuture materializedView = updatedMaterializedViewFuture.get();
+   * }
+ * + * @see UpdateMaterializedViewRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture updateMaterializedViewAsync( + UpdateMaterializedViewRequest request) { + return ApiFutures.transform( + stub.updateMaterializedViewOperationCallable().futureCall(request.toProto(projectId)), + new ApiFunction() { + @Override + public MaterializedView apply(com.google.bigtable.admin.v2.MaterializedView proto) { + return MaterializedView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Deletes the specified materialized view. + * + *

Sample code: + * + *

{@code
+   * client.deleteMaterializedView("my-instance", "my-materialized-view");
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public void deleteMaterializedView(String instanceId, String materializedViewId) { + ApiExceptions.callAndTranslateApiException( + deleteMaterializedViewAsync(instanceId, materializedViewId)); + } + + /** + * Asynchronously deletes the specified materialized view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture deleteFuture = client.deleteMaterializedViewAsync("my-instance", "my-materialized-view");
+   *
+   * deleteFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture deleteMaterializedViewAsync(String instanceId, String materializedViewId) { + + String name = NameUtil.formatMaterializedViewName(projectId, instanceId, materializedViewId); + DeleteMaterializedViewRequest request = + DeleteMaterializedViewRequest.newBuilder().setName(name).build(); + + return ApiFutures.transform( + stub.deleteMaterializedViewCallable().futureCall(request), + new ApiFunction() { + @Override + public Void apply(Empty input) { + return null; + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Creates a new logical view. + * + *

Sample code: + * + *

{@code
+   * LogicalView logicalView = client.createLogicalView(
+   *   CreateLogicalViewRequest.of("my-instance", "my-new-logical-view")
+   *     .setQuery(query)
+   * );
+   * }
+ * + * @see CreateLogicalViewRequest + */ + @SuppressWarnings("WeakerAccess") + public LogicalView createLogicalView(CreateLogicalViewRequest request) { + return ApiExceptions.callAndTranslateApiException(createLogicalViewAsync(request)); + } + + /** + * Asynchronously creates a new logical view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture logicalViewFuture = client.createLogicalViewAsync(
+   *   CreateLogicalViewRequest.of("my-instance", "my-new-logical-view")
+   *     .setQuery(query)
+   * );
+   *
+   * LogicalView logicalView = logicalViewFuture.get();
+   * }
+ * + * @see CreateLogicalViewRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture createLogicalViewAsync(CreateLogicalViewRequest request) { + return ApiFutures.transform( + stub.createLogicalViewOperationCallable().futureCall(request.toProto(projectId)), + new ApiFunction() { + @Override + public LogicalView apply(com.google.bigtable.admin.v2.LogicalView proto) { + return LogicalView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Gets the logical view by ID. + * + *

Sample code: + * + *

{@code
+   * LogicalView logicalView = client.getLogicalView("my-instance", "my-logical-view");
+   * }
+ * + * @see LogicalView + */ + public LogicalView getLogicalView(String instanceId, String logicalViewId) { + return ApiExceptions.callAndTranslateApiException( + getLogicalViewAsync(instanceId, logicalViewId)); + } + + /** + * Asynchronously gets the logical view by ID. + * + *

Sample code: + * + *

{@code
+   * ApiFuture logicalViewFuture = client.getLogicalViewAsync("my-instance", "my-logical-view");
+   *
+   * LogicalView logicalView = logicalViewFuture.get();
+   * }
+ * + * @see LogicalView + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture getLogicalViewAsync(String instanceId, String logicalViewId) { + String name = NameUtil.formatLogicalViewName(projectId, instanceId, logicalViewId); + + GetLogicalViewRequest request = GetLogicalViewRequest.newBuilder().setName(name).build(); + + return ApiFutures.transform( + stub.getLogicalViewCallable().futureCall(request), + new ApiFunction() { + @Override + public LogicalView apply(com.google.bigtable.admin.v2.LogicalView proto) { + return LogicalView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Lists all logical views of the specified instance. + * + *

Sample code: + * + *

{@code
+   * List logicalViews = client.listLogicalViews("my-instance");
+   * }
+ * + * @see LogicalView + */ + @SuppressWarnings("WeakerAccess") + public List listLogicalViews(String instanceId) { + return ApiExceptions.callAndTranslateApiException(listLogicalViewsAsync(instanceId)); + } + + /** + * Asynchronously lists all logical views of the specified instance. + * + *

Sample code: + * + *

{@code
+   * ApiFuture> logicalViewsFuture = client.listLogicalViewsAsync("my-instance");
+   *
+   * List logicalViews = logicalViewFuture.get();
+   * }
+ * + * @see LogicalView + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture> listLogicalViewsAsync(String instanceId) { + String instanceName = NameUtil.formatInstanceName(projectId, instanceId); + + ListLogicalViewsRequest request = + ListLogicalViewsRequest.newBuilder().setParent(instanceName).build(); + + // TODO(igorbernstein2): try to upstream pagination spooling or figure out a way to expose the + // paginated responses while maintaining the wrapper facade. + + // Fetches the first page. + ApiFuture firstPageFuture = + ApiFutures.transform( + stub.listLogicalViewsPagedCallable().futureCall(request), + new ApiFunction() { + @Override + public ListLogicalViewsPage apply(ListLogicalViewsPagedResponse response) { + return response.getPage(); + } + }, + MoreExecutors.directExecutor()); + + // Fetches the rest of the pages by chaining the futures. + ApiFuture> allProtos = + ApiFutures.transformAsync( + firstPageFuture, + new ApiAsyncFunction< + ListLogicalViewsPage, List>() { + List responseAccumulator = + Lists.newArrayList(); + + @Override + public ApiFuture> apply( + ListLogicalViewsPage page) { + // Add all entries from the page + responseAccumulator.addAll(Lists.newArrayList(page.getValues())); + + // If this is the last page, just return the accumulated responses. + if (!page.hasNextPage()) { + return ApiFutures.immediateFuture(responseAccumulator); + } + + // Otherwise fetch the next page. + return ApiFutures.transformAsync( + page.getNextPageAsync(), this, MoreExecutors.directExecutor()); + } + }, + MoreExecutors.directExecutor()); + + // Wraps all of the accumulated protos. + return ApiFutures.transform( + allProtos, + new ApiFunction, List>() { + @Override + public List apply(List input) { + List results = Lists.newArrayListWithCapacity(input.size()); + for (com.google.bigtable.admin.v2.LogicalView logicalView : input) { + results.add(LogicalView.fromProto(logicalView)); + } + return results; + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Updates an existing logical view. + * + *

Sample code: + * + *

{@code
+   * LogicalView existingLogicalView = client.getLogicalView("my-instance", "my-logical-view");
+   *
+   * LogicalView updatedLogicalView = client.updateLogicalView(
+   *   UpdateLogicalViewRequest.of(existingLogicalView)
+   *     .setQuery(query)
+   * );
+   * }
+ * + * @see UpdateLogicalViewRequest + */ + @SuppressWarnings("WeakerAccess") + public LogicalView updateLogicalView(UpdateLogicalViewRequest request) { + return ApiExceptions.callAndTranslateApiException(updateLogicalViewAsync(request)); + } + + /** + * Asynchronously updates an existing logical view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture existingLogicalViewFuture = client.getLogicalViewAsync("my-instance", "my-logical-view");
+   *
+   * ApiFuture updatedLogicalViewFuture = ApiFutures.transformAsync(
+   *   existingLogicalViewFuture,
+   *   new ApiAsyncFunction() {
+   *     public ApiFuture apply(LogicalView existingLogicalView) {
+   *       return client.updateLogicalViewAsync(
+   *         UpdateLogicalViewRequest.of(existingLogicalView)
+   *           .setQuery(query)
+   *       );
+   *     }
+   *   },
+   *   MoreExecutors.directExecutor()
+   * );
+   *
+   * ApiFuture logicalView = updatedLogicalViewFuture.get();
+   * }
+ * + * @see UpdateLogicalViewRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture updateLogicalViewAsync(UpdateLogicalViewRequest request) { + return ApiFutures.transform( + stub.updateLogicalViewOperationCallable().futureCall(request.toProto(projectId)), + new ApiFunction() { + @Override + public LogicalView apply(com.google.bigtable.admin.v2.LogicalView proto) { + return LogicalView.fromProto(proto); + } + }, + MoreExecutors.directExecutor()); + } + + /** + * Deletes the specified logical view. + * + *

Sample code: + * + *

{@code
+   * client.deleteLogicalView("my-instance", "my-logical-view");
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public void deleteLogicalView(String instanceId, String logicalViewId) { + ApiExceptions.callAndTranslateApiException(deleteLogicalViewAsync(instanceId, logicalViewId)); + } + + /** + * Asynchronously deletes the specified logical view. + * + *

Sample code: + * + *

{@code
+   * ApiFuture deleteFuture = client.deleteLogicalViewAsync("my-instance", "my-logical-view");
+   *
+   * deleteFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture deleteLogicalViewAsync(String instanceId, String logicalViewId) { + + String name = NameUtil.formatLogicalViewName(projectId, instanceId, logicalViewId); + DeleteLogicalViewRequest request = DeleteLogicalViewRequest.newBuilder().setName(name).build(); + + return ApiFutures.transform( + stub.deleteLogicalViewCallable().futureCall(request), + new ApiFunction() { + @Override + public Void apply(Empty input) { + return null; + } + }, + MoreExecutors.directExecutor()); + } + /** * Simple adapter to expose {@link DefaultMarshaller} to this class. It enables this client to * convert to/from IAM wrappers and protobufs. diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettings.java index 42b0ea9b5dce..974317a9d18e 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettings.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettings.java @@ -113,6 +113,16 @@ public String toString() { .add("getIamPolicySettings", stubSettings.getIamPolicySettings()) .add("setIamPolicySettings", stubSettings.setIamPolicySettings()) .add("testIamPermissionsSettings", stubSettings.testIamPermissionsSettings()) + .add("createMaterializedViewSettings", stubSettings.createMaterializedViewSettings()) + .add("getMaterializedViewSettings", stubSettings.getMaterializedViewSettings()) + .add("listMaterializedViewsSettings", stubSettings.listMaterializedViewsSettings()) + .add("updateMaterializedViewSettings", stubSettings.updateMaterializedViewSettings()) + .add("deleteMaterializedViewSettings", stubSettings.deleteMaterializedViewSettings()) + .add("createLogicalViewSettings", stubSettings.createLogicalViewSettings()) + .add("getLogicalViewSettings", stubSettings.getLogicalViewSettings()) + .add("listLogicalViewsSettings", stubSettings.listLogicalViewsSettings()) + .add("updateLogicalViewSettings", stubSettings.updateLogicalViewSettings()) + .add("deleteLogicalViewSettings", stubSettings.deleteLogicalViewSettings()) .add("stubSettings", stubSettings) .toString(); } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java index a2b59d6b5bb4..ec2c3a0b585b 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java @@ -18,6 +18,7 @@ import com.google.api.core.InternalApi; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.Nonnull; /** * Internal helper to compose full resource names. @@ -49,6 +50,16 @@ public static String formatTableName(String projectId, String instanceId, String return formatInstanceName(projectId, instanceId) + "/tables/" + tableId; } + public static String formatMaterializedViewName( + @Nonnull String projectId, @Nonnull String instanceId, @Nonnull String materializedViewId) { + return formatInstanceName(projectId, instanceId) + "/materializedViews/" + materializedViewId; + } + + public static String formatLogicalViewName( + @Nonnull String projectId, @Nonnull String instanceId, @Nonnull String logicalViewId) { + return formatInstanceName(projectId, instanceId) + "/logicalViews/" + logicalViewId; + } + public static String formatLocationName(String projectId, String zone) { return formatProjectName(projectId) + "/locations/" + zone; } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequest.java new file mode 100644 index 000000000000..9db5d80f3246 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2018 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.base.Objects; +import javax.annotation.Nonnull; + +/** + * Parameters for creating a new Cloud Bigtable logical view. + * + *

Sample code: + * + *

{@code
+ * LogicalView existingLogicalView = ...;
+ * CreateLogicalViewRequest logicalViewRequest = CreateLogicalViewRequest.of("my-instance", "my-new-logical-view")
+ *   .setQuery("...");
+ * }
+ * + * @see LogicalView for more details + */ +public final class CreateLogicalViewRequest { + private final String instanceId; + private final com.google.bigtable.admin.v2.CreateLogicalViewRequest.Builder proto; + + /** Builds a new request to create a new logical view in the specified instance. */ + public static CreateLogicalViewRequest of(String instanceId, String logicalViewId) { + return new CreateLogicalViewRequest(instanceId, logicalViewId); + } + + private CreateLogicalViewRequest(String instanceId, String logicalViewId) { + this.instanceId = instanceId; + this.proto = com.google.bigtable.admin.v2.CreateLogicalViewRequest.newBuilder(); + + proto.setLogicalViewId(logicalViewId); + } + + /** Sets the query of the LogicalView. */ + @SuppressWarnings("WeakerAccess") + public CreateLogicalViewRequest setQuery(@Nonnull String query) { + proto.getLogicalViewBuilder().setQuery(query); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateLogicalViewRequest that = (CreateLogicalViewRequest) o; + return Objects.equal(proto.build(), that.proto.build()) + && Objects.equal(instanceId, that.instanceId); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto.build(), instanceId); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.CreateLogicalViewRequest toProto(String projectId) { + String name = NameUtil.formatInstanceName(projectId, instanceId); + + return proto.setParent(name).build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequest.java new file mode 100644 index 000000000000..983a0a48e148 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.base.Objects; +import javax.annotation.Nonnull; + +/** + * Parameters for creating a new Cloud Bigtable materialized view. + * + *

Sample code: + * + *

{@code
+ * MaterializedView existingMaterializedView = ...;
+ * CreateMaterializedViewRequest materializedViewRequest = CreateMaterializedViewRequest.of("my-instance", "my-new-materialized-view")
+ *   .setQuery("...");
+ * }
+ * + * @see MaterializedView for more details + */ +public final class CreateMaterializedViewRequest { + private final String instanceId; + private final com.google.bigtable.admin.v2.CreateMaterializedViewRequest.Builder proto; + + /** Builds a new request to create a new materialized view in the specified instance. */ + public static CreateMaterializedViewRequest of(String instanceId, String materializedViewId) { + return new CreateMaterializedViewRequest(instanceId, materializedViewId); + } + + private CreateMaterializedViewRequest(String instanceId, String materializedViewId) { + this.instanceId = instanceId; + this.proto = com.google.bigtable.admin.v2.CreateMaterializedViewRequest.newBuilder(); + + proto.setMaterializedViewId(materializedViewId); + } + + /** Configures if the materialized view is deletion protected. */ + @SuppressWarnings("WeakerAccess") + public CreateMaterializedViewRequest setDeletionProtection(boolean value) { + proto.getMaterializedViewBuilder().setDeletionProtection(value); + return this; + } + + /** Sets the query of the MaterializedView. */ + @SuppressWarnings("WeakerAccess") + public CreateMaterializedViewRequest setQuery(@Nonnull String query) { + proto.getMaterializedViewBuilder().setQuery(query); + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateMaterializedViewRequest that = (CreateMaterializedViewRequest) o; + return Objects.equal(proto.build(), that.proto.build()) + && Objects.equal(instanceId, that.instanceId); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto.build(), instanceId); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.CreateMaterializedViewRequest toProto(String projectId) { + String name = NameUtil.formatInstanceName(projectId, instanceId); + + return proto.setParent(name).build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/LogicalView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/LogicalView.java new file mode 100644 index 000000000000..c884d9773004 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/LogicalView.java @@ -0,0 +1,103 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.LogicalViewName; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import javax.annotation.Nonnull; + +/** + * A class that wraps the {@link com.google.bigtable.admin.v2.LogicalView} protocol buffer object. + * + *

A LogicalView represents subsets of a particular table based on rules. The access to each + * LogicalView can be configured separately from the Table. + * + *

Users can perform read/write operation on a LogicalView by providing a logicalView id besides + * a table id, in which case the semantics remain identical as reading/writing on a Table except + * that visibility is restricted to the subset of the Table that the LogicalView represents. + */ +public final class LogicalView { + private final com.google.bigtable.admin.v2.LogicalView proto; + + /** + * Wraps the protobuf. This method is considered an internal implementation detail and not meant + * to be used by applications. + */ + @InternalApi + public static LogicalView fromProto(@Nonnull com.google.bigtable.admin.v2.LogicalView proto) { + return new LogicalView(proto); + } + + private LogicalView(@Nonnull com.google.bigtable.admin.v2.LogicalView proto) { + Preconditions.checkNotNull(proto); + Preconditions.checkArgument(!proto.getName().isEmpty(), "LogicalView must have a name"); + this.proto = proto; + } + + /** Gets the logical view's id. */ + public String getId() { + // Constructor ensures that name is not null. + LogicalViewName fullName = LogicalViewName.parse(proto.getName()); + + //noinspection ConstantConditions + return fullName.getLogicalView(); + } + + /** Gets the id of the instance that owns this LogicalView. */ + @SuppressWarnings("WeakerAccess") + public String getInstanceId() { + LogicalViewName fullName = + Verify.verifyNotNull(LogicalViewName.parse(proto.getName()), "Name can never be null"); + + //noinspection ConstantConditions + return fullName.getInstance(); + } + + /** Gets the query of this logical view. */ + public String getQuery() { + return proto.getQuery(); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.LogicalView toProto() { + return proto; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LogicalView that = (LogicalView) o; + return Objects.equal(proto, that.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/MaterializedView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/MaterializedView.java new file mode 100644 index 000000000000..c3bf494c030e --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/MaterializedView.java @@ -0,0 +1,111 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.bigtable.admin.v2.MaterializedViewName; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import javax.annotation.Nonnull; + +/** + * A class that wraps the {@link com.google.bigtable.admin.v2.MaterializedView} protocol buffer + * object. + * + *

A MaterializedView represents subsets of a particular table based on rules. The access to each + * MaterializedView can be configured separately from the Table. + * + *

Users can perform read/write operation on a MaterializedView by providing a materializedView + * id besides a table id, in which case the semantics remain identical as reading/writing on a Table + * except that visibility is restricted to the subset of the Table that the MaterializedView + * represents. + */ +public final class MaterializedView { + private final com.google.bigtable.admin.v2.MaterializedView proto; + + /** + * Wraps the protobuf. This method is considered an internal implementation detail and not meant + * to be used by applications. + */ + @InternalApi + public static MaterializedView fromProto( + @Nonnull com.google.bigtable.admin.v2.MaterializedView proto) { + return new MaterializedView(proto); + } + + private MaterializedView(@Nonnull com.google.bigtable.admin.v2.MaterializedView proto) { + Preconditions.checkNotNull(proto); + Preconditions.checkArgument(!proto.getName().isEmpty(), "MaterializedView must have a name"); + this.proto = proto; + } + + /** Gets the materialized view's id. */ + public String getId() { + // Constructor ensures that name is not null. + MaterializedViewName fullName = MaterializedViewName.parse(proto.getName()); + + //noinspection ConstantConditions + return fullName.getMaterializedView(); + } + + /** Gets the id of the instance that owns this MaterializedView. */ + @SuppressWarnings("WeakerAccess") + public String getInstanceId() { + MaterializedViewName fullName = + Verify.verifyNotNull(MaterializedViewName.parse(proto.getName()), "Name can never be null"); + + //noinspection ConstantConditions + return fullName.getInstance(); + } + + /** Returns whether this materialized view is deletion protected. */ + public boolean isDeletionProtected() { + return proto.getDeletionProtection(); + } + + /** Gets the query of this materialized view. */ + public String getQuery() { + return proto.getQuery(); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.MaterializedView toProto() { + return proto; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MaterializedView that = (MaterializedView) o; + return Objects.equal(proto, that.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequest.java new file mode 100644 index 000000000000..d24cfff30a0c --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import javax.annotation.Nonnull; + +/** + * Parameters for updating an existing Cloud Bigtable {@link LogicalView}. + * + *

Sample code: + * + *

{@code
+ * LogicalView existingLogicalView = client.getLogicalView("my-table", "my-logical-view");
+ * UpdateLogicalViewRequest request =
+ *     UpdateLogicalViewRequest.of(existingLogicalView).setQuery(query);
+ * }
+ * + * @see LogicalView for more details. + */ +public final class UpdateLogicalViewRequest { + private final com.google.bigtable.admin.v2.UpdateLogicalViewRequest.Builder requestBuilder; + private final String instanceId; + private final String logicalViewId; + + /** Builds a new update request using an existing logical view. */ + public static UpdateLogicalViewRequest of(@Nonnull LogicalView logicalView) { + return new UpdateLogicalViewRequest( + logicalView.getId(), + logicalView.getInstanceId(), + com.google.bigtable.admin.v2.UpdateLogicalViewRequest.newBuilder() + .setLogicalView(logicalView.toProto())); + } + + /** Builds a new update logical view request. */ + public static UpdateLogicalViewRequest of( + @Nonnull String instanceId, @Nonnull String logicalViewId) { + return new UpdateLogicalViewRequest( + logicalViewId, + instanceId, + com.google.bigtable.admin.v2.UpdateLogicalViewRequest.newBuilder()); + } + + private UpdateLogicalViewRequest( + @Nonnull String logicalViewId, + @Nonnull String instanceId, + @Nonnull com.google.bigtable.admin.v2.UpdateLogicalViewRequest.Builder requestBuilder) { + Preconditions.checkNotNull(instanceId, "instanceId must be set"); + Preconditions.checkNotNull(logicalViewId, "logicalViewId must be set"); + Preconditions.checkNotNull(requestBuilder, "proto builder must be set"); + + this.instanceId = instanceId; + this.logicalViewId = logicalViewId; + this.requestBuilder = requestBuilder; + } + + /** Changes the query of an existing logical view. */ + public UpdateLogicalViewRequest setQuery(String query) { + requestBuilder.getLogicalViewBuilder().setQuery(query); + updateFieldMask(com.google.bigtable.admin.v2.LogicalView.QUERY_FIELD_NUMBER); + return this; + } + + private void updateFieldMask(int fieldNumber) { + FieldMask newMask = + FieldMaskUtil.fromFieldNumbers(com.google.bigtable.admin.v2.LogicalView.class, fieldNumber); + requestBuilder.setUpdateMask(FieldMaskUtil.union(requestBuilder.getUpdateMask(), newMask)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UpdateLogicalViewRequest that = (UpdateLogicalViewRequest) o; + return Objects.equal(requestBuilder.build(), that.requestBuilder.build()) + && Objects.equal(logicalViewId, that.logicalViewId); + } + + @Override + public int hashCode() { + return Objects.hashCode(requestBuilder.build(), logicalViewId); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.UpdateLogicalViewRequest toProto(@Nonnull String projectId) { + requestBuilder + .getLogicalViewBuilder() + .setName(NameUtil.formatLogicalViewName(projectId, instanceId, logicalViewId)); + return requestBuilder.build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequest.java new file mode 100644 index 000000000000..57823da81f40 --- /dev/null +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import com.google.api.core.InternalApi; +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import javax.annotation.Nonnull; + +/** + * Parameters for updating an existing Cloud Bigtable {@link MaterializedView}. + * + *

Sample code: + * + *

{@code
+ * MaterializedView existingMaterializedView = client.getMaterializedView("my-table", "my-materialized-view");
+ * UpdateMaterializedViewRequest request =
+ *     UpdateMaterializedViewRequest.of(existingMaterializedView).setDeletionProtection(true);
+ * }
+ * + * @see MaterializedView for more details. + */ +public final class UpdateMaterializedViewRequest { + private final com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.Builder requestBuilder; + private final String instanceId; + private final String materializedViewId; + + /** Builds a new update request using an existing materialized view. */ + public static UpdateMaterializedViewRequest of(@Nonnull MaterializedView materializedView) { + return new UpdateMaterializedViewRequest( + materializedView.getId(), + materializedView.getInstanceId(), + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.newBuilder() + .setMaterializedView(materializedView.toProto())); + } + + /** Builds a new update materialized view request. */ + public static UpdateMaterializedViewRequest of( + @Nonnull String instanceId, @Nonnull String materializedViewId) { + return new UpdateMaterializedViewRequest( + materializedViewId, + instanceId, + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.newBuilder()); + } + + private UpdateMaterializedViewRequest( + @Nonnull String materializedViewId, + @Nonnull String instanceId, + @Nonnull com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.Builder requestBuilder) { + Preconditions.checkNotNull(instanceId, "instanceId must be set"); + Preconditions.checkNotNull(materializedViewId, "materializedViewId must be set"); + Preconditions.checkNotNull(requestBuilder, "proto builder must be set"); + + this.instanceId = instanceId; + this.materializedViewId = materializedViewId; + this.requestBuilder = requestBuilder; + } + + /** Changes the deletion protection of an existing materialized view. */ + public UpdateMaterializedViewRequest setDeletionProtection(boolean deletionProtection) { + requestBuilder.getMaterializedViewBuilder().setDeletionProtection(deletionProtection); + updateFieldMask(com.google.bigtable.admin.v2.MaterializedView.DELETION_PROTECTION_FIELD_NUMBER); + return this; + } + + private void updateFieldMask(int fieldNumber) { + FieldMask newMask = + FieldMaskUtil.fromFieldNumbers( + com.google.bigtable.admin.v2.MaterializedView.class, fieldNumber); + requestBuilder.setUpdateMask(FieldMaskUtil.union(requestBuilder.getUpdateMask(), newMask)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UpdateMaterializedViewRequest that = (UpdateMaterializedViewRequest) o; + return Objects.equal(requestBuilder.build(), that.requestBuilder.build()) + && Objects.equal(materializedViewId, that.materializedViewId); + } + + @Override + public int hashCode() { + return Objects.hashCode(requestBuilder.build(), materializedViewId); + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + public com.google.bigtable.admin.v2.UpdateMaterializedViewRequest toProto( + @Nonnull String projectId) { + requestBuilder + .getMaterializedViewBuilder() + .setName(NameUtil.formatMaterializedViewName(projectId, instanceId, materializedViewId)); + return requestBuilder.build(); + } +} diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java index a829c3f719d6..97c465127891 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporter.java @@ -40,9 +40,10 @@ import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.cloud.monitoring.v3.MetricServiceSettings; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.MoreExecutors; @@ -56,6 +57,7 @@ import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.io.IOException; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -94,43 +96,25 @@ public final class BigtableCloudMonitoringExporter implements MetricExporter { // https://cloud.google.com/monitoring/quotas#custom_metrics_quotas. private static final int EXPORT_BATCH_SIZE_LIMIT = 200; - private final MetricServiceClient client; + private final String exporterName; - private final String taskId; + private final MetricServiceClient client; - // Application resource is initialized on the first export, which runs on a background thread - // to avoid slowness when starting the client. - private final Supplier applicationResource; + private final TimeSeriesConverter timeSeriesConverter; private final AtomicBoolean isShutdown = new AtomicBoolean(false); private CompletableResultCode lastExportCode; - private final AtomicBoolean bigtableExportFailureLogged = new AtomicBoolean(false); - private final AtomicBoolean applicationExportFailureLogged = new AtomicBoolean(false); - - private static final ImmutableList BIGTABLE_TABLE_METRICS = - ImmutableSet.of( - OPERATION_LATENCIES_NAME, - ATTEMPT_LATENCIES_NAME, - SERVER_LATENCIES_NAME, - FIRST_RESPONSE_LATENCIES_NAME, - CLIENT_BLOCKING_LATENCIES_NAME, - APPLICATION_BLOCKING_LATENCIES_NAME, - RETRY_COUNT_NAME, - CONNECTIVITY_ERROR_COUNT_NAME, - REMAINING_DEADLINE_NAME) - .stream() - .map(m -> METER_NAME + m) - .collect(ImmutableList.toImmutableList()); - - private static final ImmutableList APPLICATION_METRICS = - ImmutableSet.of(PER_CONNECTION_ERROR_COUNT_NAME).stream() - .map(m -> METER_NAME + m) - .collect(ImmutableList.toImmutableList()); - - public static BigtableCloudMonitoringExporter create( - @Nullable Credentials credentials, @Nullable String endpoint) throws IOException { + private final AtomicBoolean exportFailureLogged = new AtomicBoolean(false); + + static BigtableCloudMonitoringExporter create( + String exporterName, + @Nullable Credentials credentials, + @Nullable String endpoint, + TimeSeriesConverter converter) + throws IOException { + MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder(); CredentialsProvider credentialsProvider = Optional.ofNullable(credentials) @@ -146,79 +130,64 @@ public static BigtableCloudMonitoringExporter create( settingsBuilder.setEndpoint(endpoint); } - java.time.Duration timeout = java.time.Duration.ofMinutes(1); + Duration timeout = Duration.ofMinutes(1); // TODO: createServiceTimeSeries needs special handling if the request failed. Leaving // it as not retried for now. settingsBuilder.createServiceTimeSeriesSettings().setSimpleTimeoutNoRetriesDuration(timeout); return new BigtableCloudMonitoringExporter( - MetricServiceClient.create(settingsBuilder.build()), - Suppliers.memoize(BigtableExporterUtils::detectResourceSafe), - BigtableExporterUtils.getDefaultTaskValue()); + exporterName, MetricServiceClient.create(settingsBuilder.build()), converter); } @VisibleForTesting BigtableCloudMonitoringExporter( - MetricServiceClient client, Supplier applicationResource, String taskId) { + String exporterName, MetricServiceClient client, TimeSeriesConverter converter) { + this.exporterName = exporterName; this.client = client; - this.taskId = taskId; - this.applicationResource = applicationResource; + this.timeSeriesConverter = converter; } @Override - public CompletableResultCode export(Collection collection) { - if (isShutdown.get()) { - logger.log(Level.WARNING, "Exporter is shutting down"); - return CompletableResultCode.ofFailure(); - } - - CompletableResultCode bigtableExportCode = exportBigtableResourceMetrics(collection); - CompletableResultCode applicationExportCode = exportApplicationResourceMetrics(collection); - - lastExportCode = - CompletableResultCode.ofAll(ImmutableList.of(applicationExportCode, bigtableExportCode)); + public CompletableResultCode export(Collection metricData) { + Preconditions.checkState(!isShutdown.get(), "Exporter is shutting down"); + lastExportCode = doExport(metricData); return lastExportCode; } /** Export metrics associated with a BigtableTable resource. */ - private CompletableResultCode exportBigtableResourceMetrics(Collection collection) { - // Filter bigtable table metrics - List bigtableMetricData = - collection.stream() - .filter(md -> BIGTABLE_TABLE_METRICS.contains(md.getName())) - .collect(Collectors.toList()); + private CompletableResultCode doExport(Collection metricData) { + Map> bigtableTimeSeries; - // Skips exporting if there's none - if (bigtableMetricData.isEmpty()) { - return CompletableResultCode.ofSuccess(); - } - - // List of timeseries by project id - Map> bigtableTimeSeries; try { - bigtableTimeSeries = - BigtableExporterUtils.convertToBigtableTimeSeries(bigtableMetricData, taskId); - } catch (Throwable e) { + bigtableTimeSeries = timeSeriesConverter.convert(metricData); + } catch (Throwable t) { logger.log( Level.WARNING, - "Failed to convert bigtable table metric data to cloud monitoring timeseries.", - e); + String.format( + "Failed to convert %s metric data to cloud monitoring timeseries.", exporterName), + t); return CompletableResultCode.ofFailure(); } - CompletableResultCode bigtableExportCode = new CompletableResultCode(); + // Skips exporting if there's none + if (bigtableTimeSeries.isEmpty()) { + return CompletableResultCode.ofSuccess(); + } + + CompletableResultCode exportCode = new CompletableResultCode(); bigtableTimeSeries.forEach( - (projectId, ts) -> { - ProjectName projectName = ProjectName.of(projectId); + (projectName, ts) -> { ApiFuture> future = exportTimeSeries(projectName, ts); ApiFutures.addCallback( future, new ApiFutureCallback>() { @Override public void onFailure(Throwable throwable) { - if (bigtableExportFailureLogged.compareAndSet(false, true)) { - String msg = "createServiceTimeSeries request failed for bigtable metrics."; + if (exportFailureLogged.compareAndSet(false, true)) { + String msg = + String.format( + "createServiceTimeSeries request failed for %s.", exporterName); if (throwable instanceof PermissionDeniedException) { msg += String.format( @@ -227,100 +196,20 @@ public void onFailure(Throwable throwable) { } logger.log(Level.WARNING, msg, throwable); } - bigtableExportCode.fail(); + exportCode.fail(); } @Override public void onSuccess(List emptyList) { // When an export succeeded reset the export failure flag to false so if there's a // transient failure it'll be logged. - bigtableExportFailureLogged.set(false); - bigtableExportCode.succeed(); + exportFailureLogged.set(false); + exportCode.succeed(); } }, MoreExecutors.directExecutor()); }); - return bigtableExportCode; - } - - /** Export metrics associated with the resource the Application is running on. */ - private CompletableResultCode exportApplicationResourceMetrics( - Collection collection) { - if (applicationResource.get() == null) { - return CompletableResultCode.ofSuccess(); - } - - // Filter application level metrics - List metricData = - collection.stream() - .filter(md -> APPLICATION_METRICS.contains(md.getName())) - .collect(Collectors.toList()); - - // Skip exporting if there's none - if (metricData.isEmpty()) { - return CompletableResultCode.ofSuccess(); - } - - List timeSeries; - try { - timeSeries = - BigtableExporterUtils.convertToApplicationResourceTimeSeries( - metricData, taskId, applicationResource.get()); - } catch (Throwable e) { - logger.log( - Level.WARNING, - "Failed to convert application metric data to cloud monitoring timeseries.", - e); - return CompletableResultCode.ofFailure(); - } - - // Construct the request. The project id will be the project id of the detected monitored - // resource. - ApiFuture> gceOrGkeFuture; - CompletableResultCode exportCode = new CompletableResultCode(); - try { - ProjectName projectName = - ProjectName.of( - applicationResource.get().getLabelsOrThrow(APPLICATION_RESOURCE_PROJECT_ID)); - - gceOrGkeFuture = exportTimeSeries(projectName, timeSeries); - - ApiFutures.addCallback( - gceOrGkeFuture, - new ApiFutureCallback>() { - @Override - public void onFailure(Throwable throwable) { - if (applicationExportFailureLogged.compareAndSet(false, true)) { - String msg = "createServiceTimeSeries request failed for bigtable metrics."; - if (throwable instanceof PermissionDeniedException) { - msg += - String.format( - " Need monitoring metric writer permission on project=%s. Follow https://cloud.google.com/bigtable/docs/client-side-metrics-setup to set up permissions.", - projectName.getProject()); - } - logger.log(Level.WARNING, msg, throwable); - } - exportCode.fail(); - } - - @Override - public void onSuccess(List emptyList) { - // When an export succeeded reset the export failure flag to false so if there's a - // transient failure it'll be logged. - applicationExportFailureLogged.set(false); - exportCode.succeed(); - } - }, - MoreExecutors.directExecutor()); - - } catch (Exception e) { - logger.log( - Level.WARNING, - "Failed to get projectName for application resource " + applicationResource); - return CompletableResultCode.ofFailure(); - } - return exportCode; } @@ -383,4 +272,87 @@ public CompletableResultCode shutdown() { public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { return AggregationTemporality.CUMULATIVE; } + + interface TimeSeriesConverter { + Map> convert(Collection metricData); + } + + static class PublicTimeSeriesConverter implements TimeSeriesConverter { + private static final ImmutableList BIGTABLE_TABLE_METRICS = + ImmutableSet.of( + OPERATION_LATENCIES_NAME, + ATTEMPT_LATENCIES_NAME, + SERVER_LATENCIES_NAME, + FIRST_RESPONSE_LATENCIES_NAME, + CLIENT_BLOCKING_LATENCIES_NAME, + APPLICATION_BLOCKING_LATENCIES_NAME, + RETRY_COUNT_NAME, + CONNECTIVITY_ERROR_COUNT_NAME, + REMAINING_DEADLINE_NAME) + .stream() + .map(m -> METER_NAME + m) + .collect(ImmutableList.toImmutableList()); + + private final String taskId; + + PublicTimeSeriesConverter() { + this(BigtableExporterUtils.DEFAULT_TABLE_VALUE.get()); + } + + PublicTimeSeriesConverter(String taskId) { + this.taskId = taskId; + } + + @Override + public Map> convert(Collection metricData) { + List relevantData = + metricData.stream() + .filter(md -> BIGTABLE_TABLE_METRICS.contains(md.getName())) + .collect(Collectors.toList()); + if (relevantData.isEmpty()) { + return ImmutableMap.of(); + } + return BigtableExporterUtils.convertToBigtableTimeSeries(relevantData, taskId); + } + } + + static class InternalTimeSeriesConverter implements TimeSeriesConverter { + private static final ImmutableList APPLICATION_METRICS = + ImmutableSet.of(PER_CONNECTION_ERROR_COUNT_NAME).stream() + .map(m -> METER_NAME + m) + .collect(ImmutableList.toImmutableList()); + + private final String taskId; + private final Supplier monitoredResource; + + InternalTimeSeriesConverter(Supplier monitoredResource) { + this(monitoredResource, BigtableExporterUtils.DEFAULT_TABLE_VALUE.get()); + } + + InternalTimeSeriesConverter(Supplier monitoredResource, String taskId) { + this.monitoredResource = monitoredResource; + this.taskId = taskId; + } + + @Override + public Map> convert(Collection metricData) { + MonitoredResource monitoredResource = this.monitoredResource.get(); + if (monitoredResource == null) { + return ImmutableMap.of(); + } + + List relevantData = + metricData.stream() + .filter(md -> APPLICATION_METRICS.contains(md.getName())) + .collect(Collectors.toList()); + if (relevantData.isEmpty()) { + return ImmutableMap.of(); + } + + return ImmutableMap.of( + ProjectName.of(monitoredResource.getLabelsOrThrow(APPLICATION_RESOURCE_PROJECT_ID)), + BigtableExporterUtils.convertToApplicationResourceTimeSeries( + relevantData, taskId, monitoredResource)); + } + } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java index 95df887f0d41..904119891cf1 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableExporterUtils.java @@ -41,8 +41,11 @@ import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableSet; import com.google.monitoring.v3.Point; +import com.google.monitoring.v3.ProjectName; import com.google.monitoring.v3.TimeInterval; import com.google.monitoring.v3.TimeSeries; import com.google.monitoring.v3.TypedValue; @@ -90,7 +93,15 @@ private BigtableExporterUtils() {} * In most cases this should look like java-${UUID}@${hostname}. The hostname will be retrieved * from the jvm name and fallback to the local hostname. */ - static String getDefaultTaskValue() { + private static String defaultTaskValue = null; + + static final Supplier DEFAULT_TABLE_VALUE = + Suppliers.memoize(BigtableExporterUtils::computeDefaultTaskValue); + + private static String computeDefaultTaskValue() { + if (defaultTaskValue != null) { + return defaultTaskValue; + } // Something like '@' final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); // If jvm doesn't have the expected format, fallback to the local hostname @@ -107,14 +118,14 @@ static String getDefaultTaskValue() { return "java-" + UUID.randomUUID() + jvmName; } - static String getProjectId(PointData pointData) { - return pointData.getAttributes().get(BIGTABLE_PROJECT_ID_KEY); + static ProjectName getProjectName(PointData pointData) { + return ProjectName.of(pointData.getAttributes().get(BIGTABLE_PROJECT_ID_KEY)); } - // Returns a list of timeseries by project id - static Map> convertToBigtableTimeSeries( - List collection, String taskId) { - Map> allTimeSeries = new HashMap<>(); + // Returns a list of timeseries by project name + static Map> convertToBigtableTimeSeries( + Collection collection, String taskId) { + Map> allTimeSeries = new HashMap<>(); for (MetricData metricData : collection) { if (!metricData.getInstrumentationScopeInfo().getName().equals(METER_NAME)) { @@ -123,11 +134,11 @@ static Map> convertToBigtableTimeSeries( } for (PointData pd : metricData.getData().getPoints()) { - String projectId = getProjectId(pd); + ProjectName projectName = getProjectName(pd); List current = - allTimeSeries.computeIfAbsent(projectId, ignored -> new ArrayList<>()); + allTimeSeries.computeIfAbsent(projectName, ignored -> new ArrayList<>()); current.add(convertPointToBigtableTimeSeries(metricData, pd, taskId)); - allTimeSeries.put(projectId, current); + allTimeSeries.put(projectName, current); } } diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java index 68836a7e718d..0e179aa86621 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsView.java @@ -17,6 +17,7 @@ import com.google.auth.Credentials; import com.google.auth.oauth2.GoogleCredentials; +import com.google.common.base.Suppliers; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; @@ -99,11 +100,26 @@ public static void registerBuiltinMetrics( public static void registerBuiltinMetrics( @Nullable Credentials credentials, SdkMeterProviderBuilder builder, @Nullable String endpoint) throws IOException { - MetricExporter metricExporter = BigtableCloudMonitoringExporter.create(credentials, endpoint); + MetricExporter publicExporter = + BigtableCloudMonitoringExporter.create( + "bigtable metrics", + credentials, + endpoint, + new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter()); + MetricExporter internalExporter = + BigtableCloudMonitoringExporter.create( + "application metrics", + credentials, + endpoint, + new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( + Suppliers.memoize(BigtableExporterUtils::detectResourceSafe))); + for (Map.Entry entry : BuiltinMetricsConstants.getAllViews().entrySet()) { builder.registerView(entry.getKey(), entry.getValue()); } - builder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + builder + .registerMetricReader(PeriodicMetricReader.create(publicExporter)) + .registerMetricReader(PeriodicMetricReader.create(internalExporter)); } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java index 7c5eb8f92741..92174437908e 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTests.java @@ -40,6 +40,10 @@ import com.google.cloud.Role; import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPage; import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPagedResponse; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListLogicalViewsPage; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListLogicalViewsPagedResponse; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListMaterializedViewsPage; +import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListMaterializedViewsPagedResponse; import com.google.cloud.bigtable.admin.v2.internal.NameUtil; import com.google.cloud.bigtable.admin.v2.models.AppProfile; import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; @@ -50,12 +54,18 @@ import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.CreateClusterRequest; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateLogicalViewRequest; +import com.google.cloud.bigtable.admin.v2.models.CreateMaterializedViewRequest; import com.google.cloud.bigtable.admin.v2.models.Instance; +import com.google.cloud.bigtable.admin.v2.models.LogicalView; +import com.google.cloud.bigtable.admin.v2.models.MaterializedView; import com.google.cloud.bigtable.admin.v2.models.PartialListClustersException; import com.google.cloud.bigtable.admin.v2.models.PartialListInstancesException; import com.google.cloud.bigtable.admin.v2.models.StorageType; import com.google.cloud.bigtable.admin.v2.models.UpdateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest; +import com.google.cloud.bigtable.admin.v2.models.UpdateLogicalViewRequest; +import com.google.cloud.bigtable.admin.v2.models.UpdateMaterializedViewRequest; import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -97,6 +107,8 @@ public class BigtableInstanceAdminClientTests { private static final String INSTANCE_ID = "my-instance"; private static final String CLUSTER_ID = "my-cluster"; private static final String APP_PROFILE_ID = "my-app-profile"; + private static final String MATERIALIZED_VIEW_ID = "my-materialized-view"; + private static final String LOGICAL_VIEW_ID = "my-logical-view"; private static final String PROJECT_NAME = NameUtil.formatProjectName(PROJECT_ID); private static final String INSTANCE_NAME = NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID); @@ -104,6 +116,10 @@ public class BigtableInstanceAdminClientTests { NameUtil.formatClusterName(PROJECT_ID, INSTANCE_ID, CLUSTER_ID); private static final String APP_PROFILE_NAME = NameUtil.formatAppProfileName(PROJECT_ID, INSTANCE_ID, APP_PROFILE_ID); + private static final String MATERIALIZED_VIEW_NAME = + NameUtil.formatMaterializedViewName(PROJECT_ID, INSTANCE_ID, MATERIALIZED_VIEW_ID); + private static final String LOGICAL_VIEW_NAME = + NameUtil.formatLogicalViewName(PROJECT_ID, INSTANCE_ID, LOGICAL_VIEW_ID); private BigtableInstanceAdminClient adminClient; @@ -231,6 +247,65 @@ public class BigtableInstanceAdminClientTests { com.google.iam.v1.TestIamPermissionsRequest, com.google.iam.v1.TestIamPermissionsResponse> mockTestIamPermissionsCallable; + @Mock + private OperationCallable< + com.google.bigtable.admin.v2.CreateMaterializedViewRequest, + com.google.bigtable.admin.v2.MaterializedView, + com.google.bigtable.admin.v2.CreateMaterializedViewMetadata> + mockCreateMaterializedViewCallable; + + @Mock + private UnaryCallable< + com.google.bigtable.admin.v2.GetMaterializedViewRequest, + com.google.bigtable.admin.v2.MaterializedView> + mockGetMaterializedViewCallable; + + @Mock + private UnaryCallable< + com.google.bigtable.admin.v2.ListMaterializedViewsRequest, + ListMaterializedViewsPagedResponse> + mockListMaterializedViewsCallable; + + @Mock + private OperationCallable< + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest, + com.google.bigtable.admin.v2.MaterializedView, + com.google.bigtable.admin.v2.UpdateMaterializedViewMetadata> + mockUpdateMaterializedViewCallable; + + @Mock + private UnaryCallable + mockDeleteMaterializedViewCallable; + + @Mock + private OperationCallable< + com.google.bigtable.admin.v2.CreateLogicalViewRequest, + com.google.bigtable.admin.v2.LogicalView, + com.google.bigtable.admin.v2.CreateLogicalViewMetadata> + mockCreateLogicalViewCallable; + + @Mock + private UnaryCallable< + com.google.bigtable.admin.v2.GetLogicalViewRequest, + com.google.bigtable.admin.v2.LogicalView> + mockGetLogicalViewCallable; + + @Mock + private UnaryCallable< + com.google.bigtable.admin.v2.ListLogicalViewsRequest, ListLogicalViewsPagedResponse> + mockListLogicalViewsCallable; + + @Mock + private OperationCallable< + com.google.bigtable.admin.v2.UpdateLogicalViewRequest, + com.google.bigtable.admin.v2.LogicalView, + com.google.bigtable.admin.v2.UpdateLogicalViewMetadata> + mockUpdateLogicalViewCallable; + + @Mock + private UnaryCallable + mockDeleteLogicalViewCallable; + @Before public void setUp() { adminClient = BigtableInstanceAdminClient.create(PROJECT_ID, mockStub); @@ -1560,4 +1635,360 @@ public void testExistsFalse() { // Verify assertThat(found).isFalse(); } + + @Test + public void testCreateMaterializedView() { + // Setup + Mockito.when(mockStub.createMaterializedViewOperationCallable()) + .thenReturn(mockCreateMaterializedViewCallable); + + com.google.bigtable.admin.v2.CreateMaterializedViewRequest expectedRequest = + com.google.bigtable.admin.v2.CreateMaterializedViewRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setMaterializedViewId(MATERIALIZED_VIEW_ID) + .setMaterializedView( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table")) + .build(); + + com.google.bigtable.admin.v2.MaterializedView expectedResponse = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table") + .build(); + + mockOperationResult(mockCreateMaterializedViewCallable, expectedRequest, expectedResponse); + + // Execute + MaterializedView actualResult = + adminClient.createMaterializedView( + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table")); + + // Verify + assertThat(actualResult).isEqualTo(MaterializedView.fromProto(expectedResponse)); + } + + @Test + public void testGetMaterializedView() { + // Setup + Mockito.when(mockStub.getMaterializedViewCallable()) + .thenReturn(mockGetMaterializedViewCallable); + + com.google.bigtable.admin.v2.GetMaterializedViewRequest expectedRequest = + com.google.bigtable.admin.v2.GetMaterializedViewRequest.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .build(); + + com.google.bigtable.admin.v2.MaterializedView expectedResponse = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table") + .build(); + + Mockito.when(mockGetMaterializedViewCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + MaterializedView actualResult = + adminClient.getMaterializedView(INSTANCE_ID, MATERIALIZED_VIEW_ID); + + // Verify + assertThat(actualResult).isEqualTo(MaterializedView.fromProto(expectedResponse)); + } + + @Test + public void testListMaterializedViews() { + // Setup + Mockito.when(mockStub.listMaterializedViewsPagedCallable()) + .thenReturn(mockListMaterializedViewsCallable); + + com.google.bigtable.admin.v2.ListMaterializedViewsRequest expectedRequest = + com.google.bigtable.admin.v2.ListMaterializedViewsRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .build(); + + // 3 MaterializedViews spread across 2 pages + List expectedProtos = Lists.newArrayList(); + for (int i = 0; i < 3; i++) { + expectedProtos.add( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(MATERIALIZED_VIEW_NAME + i) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table" + i) + .build()); + } + // 2 on the first page + ListMaterializedViewsPage page0 = Mockito.mock(ListMaterializedViewsPage.class); + Mockito.when(page0.getValues()).thenReturn(expectedProtos.subList(0, 2)); + Mockito.when(page0.hasNextPage()).thenReturn(true); + + // 1 on the last page + ListMaterializedViewsPage page1 = Mockito.mock(ListMaterializedViewsPage.class); + Mockito.when(page1.getValues()).thenReturn(expectedProtos.subList(2, 3)); + + // Link page0 to page1 + Mockito.when(page0.getNextPageAsync()).thenReturn(ApiFutures.immediateFuture(page1)); + + // Link page to the response + ListMaterializedViewsPagedResponse response0 = + Mockito.mock(ListMaterializedViewsPagedResponse.class); + Mockito.when(response0.getPage()).thenReturn(page0); + + Mockito.when(mockListMaterializedViewsCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(response0)); + + // Execute + List actualResults = adminClient.listMaterializedViews(INSTANCE_ID); + + // Verify + List expectedResults = Lists.newArrayList(); + for (com.google.bigtable.admin.v2.MaterializedView expectedProto : expectedProtos) { + expectedResults.add(MaterializedView.fromProto(expectedProto)); + } + + assertThat(actualResults).containsExactlyElementsIn(expectedResults); + } + + @Test + public void testUpdateMaterializedView() { + // Setup + Mockito.when(mockStub.updateMaterializedViewOperationCallable()) + .thenReturn(mockUpdateMaterializedViewCallable); + + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest expectedRequest = + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.newBuilder() + .setMaterializedView( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .setDeletionProtection(false)) + .setUpdateMask(FieldMask.newBuilder().addPaths("deletion_protection")) + .build(); + + com.google.bigtable.admin.v2.MaterializedView expectedResponse = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .setDeletionProtection(false) + .build(); + + mockOperationResult(mockUpdateMaterializedViewCallable, expectedRequest, expectedResponse); + + // Execute + MaterializedView actualResult = + adminClient.updateMaterializedView( + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false)); + + // Verify + assertThat(actualResult).isEqualTo(MaterializedView.fromProto(expectedResponse)); + } + + @Test + public void testDeleteMaterializedView() throws Exception { + // Setup + Mockito.when(mockStub.deleteMaterializedViewCallable()) + .thenReturn(mockDeleteMaterializedViewCallable); + + com.google.bigtable.admin.v2.DeleteMaterializedViewRequest expectedRequest = + com.google.bigtable.admin.v2.DeleteMaterializedViewRequest.newBuilder() + .setName(MATERIALIZED_VIEW_NAME) + .build(); + + final AtomicInteger wasCalled = new AtomicInteger(0); + + Mockito.when(mockDeleteMaterializedViewCallable.futureCall(expectedRequest)) + .thenAnswer( + new Answer>() { + @Override + public ApiFuture answer(InvocationOnMock invocationOnMock) { + wasCalled.incrementAndGet(); + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + }); + + // Execute + adminClient.deleteMaterializedView(INSTANCE_ID, MATERIALIZED_VIEW_ID); + + adminClient.deleteMaterializedViewAsync(INSTANCE_ID, MATERIALIZED_VIEW_ID).get(); + + // Verify + assertThat(wasCalled.get()).isEqualTo(2); + } + + @Test + public void testCreateLogicalView() { + // Setup + Mockito.when(mockStub.createLogicalViewOperationCallable()) + .thenReturn(mockCreateLogicalViewCallable); + + com.google.bigtable.admin.v2.CreateLogicalViewRequest expectedRequest = + com.google.bigtable.admin.v2.CreateLogicalViewRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setLogicalViewId(LOGICAL_VIEW_ID) + .setLogicalView( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setQuery("SELECT 1 FROM Table")) + .build(); + + com.google.bigtable.admin.v2.LogicalView expectedResponse = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .setQuery("SELECT 1 FROM Table") + .build(); + + mockOperationResult(mockCreateLogicalViewCallable, expectedRequest, expectedResponse); + + // Execute + LogicalView actualResult = + adminClient.createLogicalView( + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("SELECT 1 FROM Table")); + + // Verify + assertThat(actualResult).isEqualTo(LogicalView.fromProto(expectedResponse)); + } + + @Test + public void testGetLogicalView() { + // Setup + Mockito.when(mockStub.getLogicalViewCallable()).thenReturn(mockGetLogicalViewCallable); + + com.google.bigtable.admin.v2.GetLogicalViewRequest expectedRequest = + com.google.bigtable.admin.v2.GetLogicalViewRequest.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .build(); + + com.google.bigtable.admin.v2.LogicalView expectedResponse = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .setQuery("SELECT 1 FROM Table") + .build(); + + Mockito.when(mockGetLogicalViewCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + LogicalView actualResult = adminClient.getLogicalView(INSTANCE_ID, LOGICAL_VIEW_ID); + + // Verify + assertThat(actualResult).isEqualTo(LogicalView.fromProto(expectedResponse)); + } + + @Test + public void testListLogicalViews() { + // Setup + Mockito.when(mockStub.listLogicalViewsPagedCallable()).thenReturn(mockListLogicalViewsCallable); + + com.google.bigtable.admin.v2.ListLogicalViewsRequest expectedRequest = + com.google.bigtable.admin.v2.ListLogicalViewsRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .build(); + + // 3 LogicalViews spread across 2 pages + List expectedProtos = Lists.newArrayList(); + for (int i = 0; i < 3; i++) { + expectedProtos.add( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(LOGICAL_VIEW_NAME + i) + .setQuery("SELECT 1 FROM Table" + i) + .build()); + } + // 2 on the first page + ListLogicalViewsPage page0 = Mockito.mock(ListLogicalViewsPage.class); + Mockito.when(page0.getValues()).thenReturn(expectedProtos.subList(0, 2)); + Mockito.when(page0.hasNextPage()).thenReturn(true); + + // 1 on the last page + ListLogicalViewsPage page1 = Mockito.mock(ListLogicalViewsPage.class); + Mockito.when(page1.getValues()).thenReturn(expectedProtos.subList(2, 3)); + + // Link page0 to page1 + Mockito.when(page0.getNextPageAsync()).thenReturn(ApiFutures.immediateFuture(page1)); + + // Link page to the response + ListLogicalViewsPagedResponse response0 = Mockito.mock(ListLogicalViewsPagedResponse.class); + Mockito.when(response0.getPage()).thenReturn(page0); + + Mockito.when(mockListLogicalViewsCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(response0)); + + // Execute + List actualResults = adminClient.listLogicalViews(INSTANCE_ID); + + // Verify + List expectedResults = Lists.newArrayList(); + for (com.google.bigtable.admin.v2.LogicalView expectedProto : expectedProtos) { + expectedResults.add(LogicalView.fromProto(expectedProto)); + } + + assertThat(actualResults).containsExactlyElementsIn(expectedResults); + } + + @Test + public void testUpdateLogicalView() { + // Setup + Mockito.when(mockStub.updateLogicalViewOperationCallable()) + .thenReturn(mockUpdateLogicalViewCallable); + + com.google.bigtable.admin.v2.UpdateLogicalViewRequest expectedRequest = + com.google.bigtable.admin.v2.UpdateLogicalViewRequest.newBuilder() + .setLogicalView( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .setQuery("SELECT 1 FROM Table")) + .setUpdateMask(FieldMask.newBuilder().addPaths("query")) + .build(); + + com.google.bigtable.admin.v2.LogicalView expectedResponse = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .setQuery("SELECT 1 FROM Table") + .build(); + + mockOperationResult(mockUpdateLogicalViewCallable, expectedRequest, expectedResponse); + + // Execute + LogicalView actualResult = + adminClient.updateLogicalView( + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("SELECT 1 FROM Table")); + + // Verify + assertThat(actualResult).isEqualTo(LogicalView.fromProto(expectedResponse)); + } + + @Test + public void testDeleteLogicalView() throws Exception { + // Setup + Mockito.when(mockStub.deleteLogicalViewCallable()).thenReturn(mockDeleteLogicalViewCallable); + + com.google.bigtable.admin.v2.DeleteLogicalViewRequest expectedRequest = + com.google.bigtable.admin.v2.DeleteLogicalViewRequest.newBuilder() + .setName(LOGICAL_VIEW_NAME) + .build(); + + final AtomicInteger wasCalled = new AtomicInteger(0); + + Mockito.when(mockDeleteLogicalViewCallable.futureCall(expectedRequest)) + .thenAnswer( + new Answer>() { + @Override + public ApiFuture answer(InvocationOnMock invocationOnMock) { + wasCalled.incrementAndGet(); + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + }); + + // Execute + adminClient.deleteLogicalView(INSTANCE_ID, LOGICAL_VIEW_ID); + + adminClient.deleteLogicalViewAsync(INSTANCE_ID, LOGICAL_VIEW_ID).get(); + + // Verify + assertThat(wasCalled.get()).isEqualTo(2); + } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettingsTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettingsTest.java index 7ac632f29b4b..a19709cd073c 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettingsTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminSettingsTest.java @@ -123,6 +123,16 @@ public void testStubSettings() throws IOException { "getIamPolicySettings", "setIamPolicySettings", "testIamPermissionsSettings", + "createMaterializedViewSettings", + "getMaterializedViewSettings", + "listMaterializedViewsSettings", + "updateMaterializedViewSettings", + "deleteMaterializedViewSettings", + "createLogicalViewSettings", + "getLogicalViewSettings", + "listLogicalViewsSettings", + "updateLogicalViewSettings", + "deleteLogicalViewSettings", }; @Test diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/internal/NameUtilTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/internal/NameUtilTest.java index b21aa463c219..530077721db5 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/internal/NameUtilTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/internal/NameUtilTest.java @@ -60,6 +60,26 @@ public void formatAuthorizedViewNameTest() { .isEqualTo(testAuthorizedViewName); } + @Test + public void formatMaterializedViewNameTest() { + String testMaterializedViewName = + "projects/my-project/instances/my-instance/materializedViews/my-materialized-view"; + + assertThat( + NameUtil.formatMaterializedViewName( + "my-project", "my-instance", "my-materialized-view")) + .isEqualTo(testMaterializedViewName); + } + + @Test + public void formatLogicalViewNameTest() { + String testLogicalViewName = + "projects/my-project/instances/my-instance/logicalViews/my-logical-view"; + + assertThat(NameUtil.formatLogicalViewName("my-project", "my-instance", "my-logical-view")) + .isEqualTo(testLogicalViewName); + } + @Test public void extractAuthorizedViewIdFromAuthorizedViewNameTest() { String testAuthorizedViewName = diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequestTest.java new file mode 100644 index 000000000000..ec5f6af14f53 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateLogicalViewRequestTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CreateLogicalViewRequestTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String LOGICAL_VIEW_ID = "my-logical-view"; + + @Test + public void testToProto() { + String query = "SELECT * FROM Table"; + CreateLogicalViewRequest request = + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery(query); + + com.google.bigtable.admin.v2.CreateLogicalViewRequest requestProto = + com.google.bigtable.admin.v2.CreateLogicalViewRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setLogicalViewId(LOGICAL_VIEW_ID) + .setLogicalView(com.google.bigtable.admin.v2.LogicalView.newBuilder().setQuery(query)) + .build(); + assertThat(request.toProto(PROJECT_ID)).isEqualTo(requestProto); + } + + @Test + public void testEquality() { + CreateLogicalViewRequest request = + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("test 1"); + + assertThat(request) + .isEqualTo(CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("test 1")); + + assertThat(request) + .isNotEqualTo(CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("test 2")); + } + + @Test + public void testHashCode() { + CreateLogicalViewRequest request = + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("test 1"); + + assertThat(request.hashCode()) + .isEqualTo( + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("test 1") + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + CreateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("test 2") + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequestTest.java new file mode 100644 index 000000000000..1a116f40fdaa --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateMaterializedViewRequestTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CreateMaterializedViewRequestTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String MATERIALIZED_VIEW_ID = "my-materialized-view"; + + @Test + public void testToProto() { + String query = "SELECT * FROM Table"; + CreateMaterializedViewRequest request = + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(true) + .setQuery(query); + + com.google.bigtable.admin.v2.CreateMaterializedViewRequest requestProto = + com.google.bigtable.admin.v2.CreateMaterializedViewRequest.newBuilder() + .setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID)) + .setMaterializedViewId(MATERIALIZED_VIEW_ID) + .setMaterializedView( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setDeletionProtection(true) + .setQuery(query)) + .build(); + assertThat(request.toProto(PROJECT_ID)).isEqualTo(requestProto); + } + + @Test + public void testEquality() { + CreateMaterializedViewRequest request = + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 1") + .setDeletionProtection(false); + + assertThat(request) + .isEqualTo( + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 1") + .setDeletionProtection(false)); + + assertThat(request) + .isNotEqualTo( + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 2") + .setDeletionProtection(false)); + } + + @Test + public void testHashCode() { + CreateMaterializedViewRequest request = + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 1") + .setDeletionProtection(false); + + assertThat(request.hashCode()) + .isEqualTo( + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 1") + .setDeletionProtection(false) + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + CreateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setQuery("test 2") + .setDeletionProtection(false) + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/LogicalViewTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/LogicalViewTest.java new file mode 100644 index 000000000000..8b802ec8d7c5 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/LogicalViewTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.LogicalViewName; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class LogicalViewTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String LOGICAL_VIEW_ID = "my-logical-view"; + + @Test + public void testFromProto() { + LogicalViewName logicalViewName = LogicalViewName.of(PROJECT_ID, INSTANCE_ID, LOGICAL_VIEW_ID); + + com.google.bigtable.admin.v2.LogicalView logicalViewProto = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(logicalViewName.toString()) + .setQuery("SELECT 1 from Table") + .build(); + + LogicalView result = LogicalView.fromProto(logicalViewProto); + + assertThat(result.getId()).isEqualTo(LOGICAL_VIEW_ID); + assertThat(result.getQuery()).isEqualTo("SELECT 1 from Table"); + } + + @Test + public void testRequiresName() { + com.google.bigtable.admin.v2.LogicalView proto = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setQuery("SELECT 1 FROM Table") + .build(); + + Exception actualException = null; + + try { + LogicalView.fromProto(proto); + } catch (Exception e) { + actualException = e; + } + + assertThat(actualException).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testEquality() { + LogicalViewName logicalViewName = LogicalViewName.of(PROJECT_ID, INSTANCE_ID, LOGICAL_VIEW_ID); + com.google.bigtable.admin.v2.LogicalView proto = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(logicalViewName.toString()) + .setQuery("SELECT 1 FROM Table") + .build(); + LogicalView logicalView = LogicalView.fromProto(proto); + + assertThat(logicalView).isEqualTo(LogicalView.fromProto(proto)); + + assertThat(logicalView) + .isNotEqualTo( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(logicalViewName.toString()) + .setQuery("SELECT 2 FROM Table") + .build()); + } + + @Test + public void testHashCode() { + LogicalViewName logicalViewName = LogicalViewName.of(PROJECT_ID, INSTANCE_ID, LOGICAL_VIEW_ID); + com.google.bigtable.admin.v2.LogicalView proto = + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(logicalViewName.toString()) + .setQuery("SELECT 1 FROM Table") + .build(); + LogicalView logicalView = LogicalView.fromProto(proto); + + assertThat(logicalView.hashCode()).isEqualTo(LogicalView.fromProto(proto).hashCode()); + + assertThat(logicalView.hashCode()) + .isNotEqualTo( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setName(logicalViewName.toString()) + .setQuery("SELECT 2 FROM Table") + .build() + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/MaterializedViewTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/MaterializedViewTest.java new file mode 100644 index 000000000000..548be93f8c74 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/MaterializedViewTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.MaterializedViewName; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class MaterializedViewTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String MATERIALIZED_VIEW_ID = "my-materialized-view"; + + @Test + public void testFromProto() { + MaterializedViewName materializedViewName = + MaterializedViewName.of(PROJECT_ID, INSTANCE_ID, MATERIALIZED_VIEW_ID); + + com.google.bigtable.admin.v2.MaterializedView materializedViewProto = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(materializedViewName.toString()) + .setDeletionProtection(true) + .setQuery("SELECT 1 from Table") + .build(); + + MaterializedView result = MaterializedView.fromProto(materializedViewProto); + + assertThat(result.getId()).isEqualTo(MATERIALIZED_VIEW_ID); + assertThat(result.isDeletionProtected()).isTrue(); + assertThat(result.getQuery()).isEqualTo("SELECT 1 from Table"); + } + + @Test + public void testRequiresName() { + com.google.bigtable.admin.v2.MaterializedView proto = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setDeletionProtection(true) + .setQuery("SELECT 1 FROM Table") + .build(); + + Exception actualException = null; + + try { + MaterializedView.fromProto(proto); + } catch (Exception e) { + actualException = e; + } + + assertThat(actualException).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testEquality() { + MaterializedViewName materializedViewName = + MaterializedViewName.of(PROJECT_ID, INSTANCE_ID, MATERIALIZED_VIEW_ID); + com.google.bigtable.admin.v2.MaterializedView proto = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(materializedViewName.toString()) + .setDeletionProtection(true) + .setQuery("SELECT 1 FROM Table") + .build(); + MaterializedView materializedView = MaterializedView.fromProto(proto); + + assertThat(materializedView).isEqualTo(MaterializedView.fromProto(proto)); + + assertThat(materializedView) + .isNotEqualTo( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(materializedViewName.toString()) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table") + .build()); + } + + @Test + public void testHashCode() { + MaterializedViewName materializedViewName = + MaterializedViewName.of(PROJECT_ID, INSTANCE_ID, MATERIALIZED_VIEW_ID); + com.google.bigtable.admin.v2.MaterializedView proto = + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(materializedViewName.toString()) + .setDeletionProtection(true) + .setQuery("SELECT 1 FROM Table") + .build(); + MaterializedView materializedView = MaterializedView.fromProto(proto); + + assertThat(materializedView.hashCode()).isEqualTo(MaterializedView.fromProto(proto).hashCode()); + + assertThat(materializedView.hashCode()) + .isNotEqualTo( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setName(materializedViewName.toString()) + .setDeletionProtection(false) + .setQuery("SELECT 1 FROM Table") + .build() + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequestTest.java new file mode 100644 index 000000000000..6421d9cf5631 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateLogicalViewRequestTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.protobuf.FieldMask; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class UpdateLogicalViewRequestTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String LOGICAL_VIEW_ID = "my-logical-view"; + + @Test + public void testToProto() { + UpdateLogicalViewRequest request = + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("query 1"); + + com.google.bigtable.admin.v2.UpdateLogicalViewRequest requestProto = + com.google.bigtable.admin.v2.UpdateLogicalViewRequest.newBuilder() + .setLogicalView( + com.google.bigtable.admin.v2.LogicalView.newBuilder() + .setQuery("query 1") + .setName( + NameUtil.formatLogicalViewName(PROJECT_ID, INSTANCE_ID, LOGICAL_VIEW_ID))) + .setUpdateMask(FieldMask.newBuilder().addPaths("query").build()) + .build(); + assertThat(request.toProto(PROJECT_ID)).isEqualTo(requestProto); + } + + @Test + public void testEquality() { + UpdateLogicalViewRequest request = + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("query 1"); + + assertThat(request) + .isEqualTo(UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("query 1")); + + assertThat(request) + .isNotEqualTo( + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("query 2")); + } + + @Test + public void testHashCode() { + UpdateLogicalViewRequest request = + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID).setQuery("query 1"); + + assertThat(request.hashCode()) + .isEqualTo( + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("query 1") + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + UpdateLogicalViewRequest.of(INSTANCE_ID, LOGICAL_VIEW_ID) + .setQuery("query 2") + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequestTest.java new file mode 100644 index 000000000000..17cbecea9ae6 --- /dev/null +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateMaterializedViewRequestTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2024 Google LLC + * + * 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 + * + * https://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.bigtable.admin.v2.models; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.bigtable.admin.v2.internal.NameUtil; +import com.google.protobuf.FieldMask; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class UpdateMaterializedViewRequestTest { + private static final String PROJECT_ID = "my-project"; + private static final String INSTANCE_ID = "my-instance"; + private static final String MATERIALIZED_VIEW_ID = "my-materialized-view"; + + @Test + public void testToProto() { + UpdateMaterializedViewRequest request = + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(true); + + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest requestProto = + com.google.bigtable.admin.v2.UpdateMaterializedViewRequest.newBuilder() + .setMaterializedView( + com.google.bigtable.admin.v2.MaterializedView.newBuilder() + .setDeletionProtection(true) + .setName( + NameUtil.formatMaterializedViewName( + PROJECT_ID, INSTANCE_ID, MATERIALIZED_VIEW_ID))) + .setUpdateMask(FieldMask.newBuilder().addPaths("deletion_protection").build()) + .build(); + assertThat(request.toProto(PROJECT_ID)).isEqualTo(requestProto); + } + + @Test + public void testEquality() { + UpdateMaterializedViewRequest request = + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false); + + assertThat(request) + .isEqualTo( + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false)); + + assertThat(request) + .isNotEqualTo( + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(true)); + } + + @Test + public void testHashCode() { + UpdateMaterializedViewRequest request = + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false); + + assertThat(request.hashCode()) + .isEqualTo( + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(false) + .hashCode()); + + assertThat(request.hashCode()) + .isNotEqualTo( + UpdateMaterializedViewRequest.of(INSTANCE_ID, MATERIALIZED_VIEW_ID) + .setDeletionProtection(true) + .hashCode()); + } +} diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/FakeServiceBuilder.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/FakeServiceBuilder.java index 5edcca2f07df..c2b4edf763b4 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/FakeServiceBuilder.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/FakeServiceBuilder.java @@ -64,9 +64,13 @@ public Server start() throws IOException { return startWithoutRetries(); } catch (IOException e) { lastError = e; - if (!(e.getCause() instanceof BindException)) { - break; + if (e.getCause() instanceof BindException) { + continue; } + if (e.getMessage().contains("Failed to bind to address")) { + continue; + } + break; } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/DynamicFlowControlCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/DynamicFlowControlCallableTest.java index 0083d94d1273..f9c1c89a5113 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/DynamicFlowControlCallableTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/DynamicFlowControlCallableTest.java @@ -100,10 +100,11 @@ public void cleanup() { @Test public void testLatenciesAreRecorded() throws Exception { - DynamicFlowControlStats stats = new DynamicFlowControlStats(); DynamicFlowControlCallable callableToTest = new DynamicFlowControlCallable( - innerCallable, flowController, stats, TARGET_LATENCY_MS, ADJUSTING_INTERVAL_MS); + // significantly increase targetLatency to ensure that slow CI runners dont accidentally + // trigger a resize + innerCallable, flowController, stats, TARGET_LATENCY_MS * 10, ADJUSTING_INTERVAL_MS); Map> extraHeaders = new HashMap<>(); extraHeaders.put(LATENCY_HEADER, Arrays.asList("5")); ApiCallContext newContext = context.withExtraHeaders(extraHeaders); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java index fcdb4a06243c..099c034d14a2 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStubTest.java @@ -99,6 +99,7 @@ import io.grpc.CallOptions; import io.grpc.Context; import io.grpc.Deadline; +import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.Metadata; import io.grpc.Metadata.Key; @@ -238,6 +239,9 @@ public void testBatchJwtAudience() .setPrivateKeyId("fake-private-key") .build(); + ManagedChannel channel = + ManagedChannelBuilder.forAddress("localhost", server.getPort()).usePlaintext().build(); + EnhancedBigtableStubSettings settings = EnhancedBigtableStubSettings.newBuilder() .setProjectId("fake-project") @@ -247,11 +251,7 @@ public void testBatchJwtAudience() .setMetricsProvider(NoopMetricsProvider.INSTANCE) // Use a fixed channel that will ignore the default endpoint and connect to the emulator .setTransportChannelProvider( - FixedTransportChannelProvider.create( - GrpcTransportChannel.create( - ManagedChannelBuilder.forAddress("localhost", server.getPort()) - .usePlaintext() - .build()))) + FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel))) // Channel refreshing doesn't work with FixedTransportChannelProvider. Disable it for // the test .setRefreshingChannel(false) @@ -263,6 +263,7 @@ public void testBatchJwtAudience() stub.readRowCallable().futureCall(Query.create("fake-table")).get(); metadata = metadataInterceptor.headers.take(); } + channel.shutdown(); String authValue = metadata.get(Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER)); String expectedPrefix = "Bearer "; diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java index e471b19a2043..8e429f2f3ef7 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableCloudMonitoringExporterTest.java @@ -96,7 +96,9 @@ public void setUp() { exporter = new BigtableCloudMonitoringExporter( - fakeMetricServiceClient, /* applicationResource= */ Suppliers.ofInstance(null), taskId); + "bigtable metrics", + fakeMetricServiceClient, + new BigtableCloudMonitoringExporter.PublicTimeSeriesConverter(taskId)); attributes = Attributes.builder() @@ -308,14 +310,16 @@ public void testTimeSeriesForMetricWithGceOrGkeResource() { String gceProjectId = "fake-gce-project"; BigtableCloudMonitoringExporter exporter = new BigtableCloudMonitoringExporter( + "application metrics", fakeMetricServiceClient, - Suppliers.ofInstance( - MonitoredResource.newBuilder() - .setType("gce-instance") - .putLabels("some-gce-key", "some-gce-value") - .putLabels("project_id", gceProjectId) - .build()), - taskId); + new BigtableCloudMonitoringExporter.InternalTimeSeriesConverter( + Suppliers.ofInstance( + MonitoredResource.newBuilder() + .setType("gce-instance") + .putLabels("some-gce-key", "some-gce-value") + .putLabels("project_id", gceProjectId) + .build()), + taskId)); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CreateTimeSeriesRequest.class); diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java index c2b2d37af672..2682f753f791 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BuiltinMetricsTracerTest.java @@ -35,9 +35,11 @@ import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.getMetricData; import static com.google.cloud.bigtable.data.v2.stub.metrics.BuiltinMetricsTestUtils.verifyAttributes; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import com.google.api.client.util.Lists; import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; import com.google.api.core.SettableApiFuture; import com.google.api.gax.batching.Batcher; import com.google.api.gax.batching.BatchingException; @@ -68,6 +70,7 @@ import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub; import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings; import com.google.common.base.Stopwatch; +import com.google.common.collect.Comparators; import com.google.common.collect.Range; import com.google.protobuf.ByteString; import com.google.protobuf.BytesValue; @@ -98,6 +101,7 @@ import java.net.SocketAddress; import java.nio.charset.Charset; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -134,7 +138,7 @@ public class BuiltinMetricsTracerTest { private static final long APPLICATION_LATENCY = 200; private static final long SLEEP_VARIABILITY = 15; private static final String CLIENT_NAME = "java-bigtable/" + Version.VERSION; - private static final long CHANNEL_BLOCKING_LATENCY = 200; + private static final Duration CHANNEL_BLOCKING_LATENCY = Duration.ofMillis(200); @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -149,6 +153,8 @@ public class BuiltinMetricsTracerTest { private InMemoryMetricReader metricReader; + private DelayProxyDetector delayProxyDetector; + @Before public void setUp() throws Exception { metricReader = InMemoryMetricReader.create(); @@ -253,15 +259,16 @@ public void sendHeaders(Metadata headers) { final ApiFunction oldConfigurator = channelProvider.getChannelConfigurator(); + delayProxyDetector = new DelayProxyDetector(); + channelProvider.setChannelConfigurator( (builder) -> { if (oldConfigurator != null) { builder = oldConfigurator.apply(builder); } - return builder.proxyDetector(new DelayProxyDetector()); + return builder.proxyDetector(delayProxyDetector); }); stubSettingsBuilder.setTransportChannelProvider(channelProvider.build()); - EnhancedBigtableStubSettings stubSettings = stubSettingsBuilder.build(); stub = new EnhancedBigtableStub(stubSettings, ClientContext.create(stubSettings)); } @@ -696,8 +703,10 @@ public void testBatchBlockingLatencies() throws InterruptedException { } @Test - public void testQueuedOnChannelServerStreamLatencies() { - stub.readRowsCallable().all().call(Query.create(TABLE)); + public void testQueuedOnChannelServerStreamLatencies() throws Exception { + ApiFuture> f = stub.readRowsCallable().all().futureCall(Query.create(TABLE)); + Duration proxyDelayPriorTest = delayProxyDetector.getCurrentDelayUsed(); + f.get(); MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME); @@ -711,14 +720,20 @@ public void testQueuedOnChannelServerStreamLatencies() { .put(CLIENT_NAME_KEY, CLIENT_NAME) .build(); - long value = getAggregatedValue(clientLatency, attributes); - assertThat(value).isAtLeast(CHANNEL_BLOCKING_LATENCY); + assertThat(Duration.ofMillis(getAggregatedValue(clientLatency, attributes))) + .isAtLeast( + // Offset the expected latency to deal with asynchrony and jitter + CHANNEL_BLOCKING_LATENCY.minus( + Comparators.max(proxyDelayPriorTest, Duration.ofMillis(1)))); } @Test - public void testQueuedOnChannelUnaryLatencies() { - - stub.mutateRowCallable().call(RowMutation.create(TABLE, "a-key").setCell("f", "q", "v")); + public void testQueuedOnChannelUnaryLatencies() throws Exception { + ApiFuture f = + stub.mutateRowCallable() + .futureCall(RowMutation.create(TABLE, "a-key").setCell("f", "q", "v")); + Duration proxyDelayPriorTest = delayProxyDetector.getCurrentDelayUsed(); + f.get(); MetricData clientLatency = getMetricData(metricReader, CLIENT_BLOCKING_LATENCIES_NAME); @@ -732,8 +747,11 @@ public void testQueuedOnChannelUnaryLatencies() { .put(CLIENT_NAME_KEY, CLIENT_NAME) .build(); - long actual = getAggregatedValue(clientLatency, attributes); - assertThat(actual).isAtLeast(CHANNEL_BLOCKING_LATENCY); + assertThat(Duration.ofMillis(getAggregatedValue(clientLatency, attributes))) + .isAtLeast( + // Offset the expected latency to deal with asynchrony and jitter + CHANNEL_BLOCKING_LATENCY.minus( + Comparators.max(proxyDelayPriorTest, Duration.ofMillis(1)))); } @Test @@ -809,7 +827,7 @@ public void testRemainingDeadline() { double okRemainingDeadline = okHistogramPointData.getSum(); // first attempt latency + retry delay - double expected = 9000 - SERVER_LATENCY - CHANNEL_BLOCKING_LATENCY - 10; + double expected = 9000 - SERVER_LATENCY - CHANNEL_BLOCKING_LATENCY.toMillis() - 10; assertThat(okRemainingDeadline).isIn(Range.closed(expected - 500, expected + 10)); } @@ -934,16 +952,33 @@ public AtomicInteger getResponseCounter() { } class DelayProxyDetector implements ProxyDetector { + private volatile Instant lastProxyDelay = null; @Nullable @Override public ProxiedSocketAddress proxyFor(SocketAddress socketAddress) throws IOException { + lastProxyDelay = Instant.now(); try { - Thread.sleep(CHANNEL_BLOCKING_LATENCY); + Thread.sleep(CHANNEL_BLOCKING_LATENCY.toMillis()); } catch (InterruptedException e) { } return null; } + + Duration getCurrentDelayUsed() { + Instant local = lastProxyDelay; + // If the delay was never injected + if (local == null) { + return Duration.ZERO; + } + Duration duration = Duration.between(local, Instant.now()); + + assertWithMessage("test burned through all channel blocking latency during setup") + .that(duration) + .isLessThan(CHANNEL_BLOCKING_LATENCY); + + return duration; + } } } diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsHeadersCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsHeadersCallableTest.java index 99b0ab5b5e47..7c6f34bb26f4 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsHeadersCallableTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/StatsHeadersCallableTest.java @@ -97,8 +97,12 @@ public void setUp() throws Exception { @After public void tearDown() { - stub.close(); - server.shutdown(); + if (stub != null) { + stub.close(); + } + if (server != null) { + server.shutdown(); + } } @Test diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsRetryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsRetryTest.java index 86a94d34eaec..cb3d49f0b279 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsRetryTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsRetryTest.java @@ -28,6 +28,7 @@ import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.data.v2.models.BulkMutation; import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider; import com.google.common.collect.Queues; import io.grpc.Status; import io.grpc.StatusRuntimeException; @@ -62,7 +63,8 @@ public void setUp() throws IOException { BigtableDataSettings.newBuilder() .setProjectId("fake-project") .setInstanceId("fake-instance") - .setCredentialsProvider(NoCredentialsProvider.create()); + .setCredentialsProvider(NoCredentialsProvider.create()) + .setMetricsProvider(NoopMetricsProvider.INSTANCE); settings .stubSettings() diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/readrows/ReadRowsRetryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/readrows/ReadRowsRetryTest.java index 3ff77a3f5d25..094789ebc4e6 100644 --- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/readrows/ReadRowsRetryTest.java +++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/readrows/ReadRowsRetryTest.java @@ -36,6 +36,7 @@ import com.google.cloud.bigtable.data.v2.models.Query; import com.google.cloud.bigtable.data.v2.models.Range.ByteStringRange; import com.google.cloud.bigtable.data.v2.models.Row; +import com.google.cloud.bigtable.data.v2.stub.metrics.NoopMetricsProvider; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Range; @@ -86,7 +87,8 @@ public void setUp() throws IOException { BigtableDataSettings.newBuilder() .setProjectId(PROJECT_ID) .setInstanceId(INSTANCE_ID) - .setCredentialsProvider(NoCredentialsProvider.create()); + .setCredentialsProvider(NoCredentialsProvider.create()) + .setMetricsProvider(NoopMetricsProvider.INSTANCE); settings .stubSettings() diff --git a/grpc-google-cloud-bigtable-admin-v2/pom.xml b/grpc-google-cloud-bigtable-admin-v2/pom.xml index 525357692374..3e4155b22fc1 100644 --- a/grpc-google-cloud-bigtable-admin-v2/pom.xml +++ b/grpc-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 grpc-google-cloud-bigtable-admin-v2 GRPC library for grpc-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import diff --git a/grpc-google-cloud-bigtable-v2/pom.xml b/grpc-google-cloud-bigtable-v2/pom.xml index de347512e1a1..c367051affee 100644 --- a/grpc-google-cloud-bigtable-v2/pom.xml +++ b/grpc-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 grpc-google-cloud-bigtable-v2 GRPC library for grpc-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import diff --git a/pom.xml b/pom.xml index b673fa95d605..8dadbba4a1f5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ google-cloud-bigtable-parent pom - 2.55.0 + 2.56.0 Google Cloud Bigtable Parent https://github.com/googleapis/java-bigtable @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.44.0 + 3.45.1 @@ -153,27 +153,27 @@ com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 com.google.api.grpc grpc-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 com.google.api.grpc grpc-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 com.google.cloud google-cloud-bigtable - 2.55.0 + 2.56.0 diff --git a/proto-google-cloud-bigtable-admin-v2/pom.xml b/proto-google-cloud-bigtable-admin-v2/pom.xml index bf0b7f6297bf..ebf9e4383147 100644 --- a/proto-google-cloud-bigtable-admin-v2/pom.xml +++ b/proto-google-cloud-bigtable-admin-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-admin-v2 - 2.55.0 + 2.56.0 proto-google-cloud-bigtable-admin-v2 PROTO library for proto-google-cloud-bigtable-admin-v2 com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import diff --git a/proto-google-cloud-bigtable-v2/pom.xml b/proto-google-cloud-bigtable-v2/pom.xml index 83e09a001713..db373b54471a 100644 --- a/proto-google-cloud-bigtable-v2/pom.xml +++ b/proto-google-cloud-bigtable-v2/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-bigtable-v2 - 2.55.0 + 2.56.0 proto-google-cloud-bigtable-v2 PROTO library for proto-google-cloud-bigtable-v2 com.google.cloud google-cloud-bigtable-parent - 2.55.0 + 2.56.0 @@ -18,14 +18,14 @@ com.google.cloud google-cloud-bigtable-deps-bom - 2.55.0 + 2.56.0 pom import com.google.cloud google-cloud-bigtable-bom - 2.55.0 + 2.56.0 pom import diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 813f0200745f..b40edd6dbb56 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -28,7 +28,7 @@ com.google.cloud google-cloud-bigtable - 2.55.0 + 2.56.0 diff --git a/test-proxy/pom.xml b/test-proxy/pom.xml index a6c342423e7a..429ad8110e88 100644 --- a/test-proxy/pom.xml +++ b/test-proxy/pom.xml @@ -12,11 +12,11 @@ google-cloud-bigtable-parent com.google.cloud - 2.55.0 + 2.56.0 - 2.55.0 + 2.56.0 diff --git a/versions.txt b/versions.txt index dc223119e488..60997341ac59 100644 --- a/versions.txt +++ b/versions.txt @@ -1,10 +1,10 @@ # Format: # module:released-version:current-version -google-cloud-bigtable:2.55.0:2.55.0 -grpc-google-cloud-bigtable-admin-v2:2.55.0:2.55.0 -grpc-google-cloud-bigtable-v2:2.55.0:2.55.0 -proto-google-cloud-bigtable-admin-v2:2.55.0:2.55.0 -proto-google-cloud-bigtable-v2:2.55.0:2.55.0 -google-cloud-bigtable-emulator:0.192.0:0.192.0 -google-cloud-bigtable-emulator-core:0.192.0:0.192.0 +google-cloud-bigtable:2.56.0:2.56.0 +grpc-google-cloud-bigtable-admin-v2:2.56.0:2.56.0 +grpc-google-cloud-bigtable-v2:2.56.0:2.56.0 +proto-google-cloud-bigtable-admin-v2:2.56.0:2.56.0 +proto-google-cloud-bigtable-v2:2.56.0:2.56.0 +google-cloud-bigtable-emulator:0.193.0:0.193.0 +google-cloud-bigtable-emulator-core:0.193.0:0.193.0