diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java index 9246533ed6ca..f4f85639062b 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java @@ -15,31 +15,34 @@ */ package com.google.cloud.bigtable.admin.v2; +import com.google.api.core.ApiAsyncFunction; import com.google.api.core.ApiFunction; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.bigtable.admin.v2.AppProfileName; import com.google.bigtable.admin.v2.ClusterName; -import com.google.bigtable.admin.v2.DeleteClusterRequest; -import com.google.bigtable.admin.v2.DeleteInstanceRequest; -import com.google.bigtable.admin.v2.GetClusterRequest; -import com.google.bigtable.admin.v2.GetInstanceRequest; +import com.google.bigtable.admin.v2.DeleteAppProfileRequest; +import com.google.bigtable.admin.v2.GetAppProfileRequest; import com.google.bigtable.admin.v2.InstanceName; -import com.google.bigtable.admin.v2.ListClustersRequest; -import com.google.bigtable.admin.v2.ListClustersResponse; -import com.google.bigtable.admin.v2.ListInstancesRequest; -import com.google.bigtable.admin.v2.ListInstancesResponse; +import com.google.bigtable.admin.v2.ListAppProfilesRequest; import com.google.bigtable.admin.v2.LocationName; import com.google.bigtable.admin.v2.ProjectName; +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.models.AppProfile; import com.google.cloud.bigtable.admin.v2.models.Cluster; +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.Instance; 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.stub.BigtableInstanceAdminStub; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.UncheckedExecutionException; @@ -257,7 +260,7 @@ public Instance getInstance(String id) { public ApiFuture getInstanceAsync(String instanceId) { InstanceName name = InstanceName.of(projectName.getProject(), instanceId); - GetInstanceRequest request = GetInstanceRequest.newBuilder() + com.google.bigtable.admin.v2.GetInstanceRequest request = com.google.bigtable.admin.v2.GetInstanceRequest.newBuilder() .setName(name.toString()) .build(); @@ -325,17 +328,18 @@ public List listInstances() { */ @SuppressWarnings("WeakerAccess") public ApiFuture> listInstancesAsync() { - ListInstancesRequest request = ListInstancesRequest.newBuilder() - .setParent(projectName.toString()) - .build(); + com.google.bigtable.admin.v2.ListInstancesRequest request = + com.google.bigtable.admin.v2.ListInstancesRequest.newBuilder() + .setParent(projectName.toString()) + .build(); - ApiFuture responseFuture = stub.listInstancesCallable() - .futureCall(request); + ApiFuture responseFuture = + stub.listInstancesCallable().futureCall(request); return ApiFutures - .transform(responseFuture, new ApiFunction>() { + .transform(responseFuture, new ApiFunction>() { @Override - public List apply(ListInstancesResponse proto) { + public List apply(com.google.bigtable.admin.v2.ListInstancesResponse proto) { // NOTE: pagination is intentionally ignored. The server does not implement it and never // will. Verify.verify(proto.getNextPageToken().isEmpty(), @@ -390,9 +394,10 @@ public void deleteInstance(String instanceId) { public ApiFuture deleteInstanceAsync(String instanceId) { InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId); - DeleteInstanceRequest request = DeleteInstanceRequest.newBuilder() - .setName(instanceName.toString()) - .build(); + com.google.bigtable.admin.v2.DeleteInstanceRequest request = + com.google.bigtable.admin.v2.DeleteInstanceRequest.newBuilder() + .setName(instanceName.toString()) + .build(); return ApiFutures.transform(stub.deleteInstanceCallable().futureCall(request), new ApiFunction() { @@ -482,9 +487,10 @@ public Cluster getCluster(String instanceId, String clusterId) { public ApiFuture getClusterAsync(String instanceId, String clusterId) { ClusterName name = ClusterName.of(projectName.getProject(), instanceId, clusterId); - GetClusterRequest request = GetClusterRequest.newBuilder() - .setName(name.toString()) - .build(); + com.google.bigtable.admin.v2.GetClusterRequest request = + com.google.bigtable.admin.v2.GetClusterRequest.newBuilder() + .setName(name.toString()) + .build(); return ApiFutures.transform( stub.getClusterCallable().futureCall(request), @@ -551,15 +557,16 @@ public List listClusters(String instanceId) { @SuppressWarnings("WeakerAccess") public ApiFuture> listClustersAsync(String instanceId) { InstanceName name = InstanceName.of(projectName.getProject(), instanceId); - ListClustersRequest request = ListClustersRequest.newBuilder() - .setParent(name.toString()) - .build(); + com.google.bigtable.admin.v2.ListClustersRequest request = + com.google.bigtable.admin.v2.ListClustersRequest.newBuilder() + .setParent(name.toString()) + .build(); return ApiFutures.transform( stub.listClustersCallable().futureCall(request), - new ApiFunction>() { + new ApiFunction>() { @Override - public List apply(ListClustersResponse proto) { + public List apply(com.google.bigtable.admin.v2.ListClustersResponse proto) { // NOTE: serverside pagination is not and will not be implemented, so remaining pages // are not fetched. However, if that assumption turns out to be wrong, fail fast to // avoid returning partial data. @@ -665,12 +672,316 @@ public void deleteCluster(String instanceId, String clusterId) { public ApiFuture deleteClusterAsync(String instanceId, String clusterId) { ClusterName name = ClusterName.of(projectName.getProject(), instanceId, clusterId); - DeleteClusterRequest request = DeleteClusterRequest.newBuilder() + com.google.bigtable.admin.v2.DeleteClusterRequest request = + com.google.bigtable.admin.v2.DeleteClusterRequest.newBuilder() + .setName(name.toString()) + .build(); + + return ApiFutures.transform( + stub.deleteClusterCallable().futureCall(request), + new ApiFunction() { + @Override + public Void apply(Empty input) { + return null; + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Creates a new app profile. + * + *

Sample code: + * + *

{@code
+   * AppProfile appProfile = client.createAppProfile(
+   *   CreateAppProfileRequest.of("my-instance", "my-new-app-profile")
+   *     .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
+   * );
+   * }
+ * + * @see CreateAppProfileRequest + */ + @SuppressWarnings("WeakerAccess") + public AppProfile createAppProfile(CreateAppProfileRequest request) { + return awaitFuture(createAppProfileAsync(request)); + } + + /** + * Asynchronously creates a new app profile. + * + *

Sample code: + * + *

{@code
+   * ApiFuture appProfileFuture = client.createAppProfileAsync(
+   *   CreateAppProfileRequest.of("my-instance", "my-new-app-profile")
+   *     .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
+   * );
+   *
+   * AppProfile appProfile = appProfileFuture.get();
+   * }
+ * + * @see CreateAppProfileRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture createAppProfileAsync(CreateAppProfileRequest request) { + return ApiFutures.transform( + stub.createAppProfileCallable().futureCall(request.toProto(projectName)), + new ApiFunction() { + @Override + public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) { + return AppProfile.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Get the app profile by id. + * + *

Sample code: + * + *

{@code
+   * AppProfile appProfile = client.getAppProfile("my-instance", "my-app-profile");
+   * }
+ * + * @see AppProfile + */ + @SuppressWarnings("WeakerAccess") + public AppProfile getAppProfile(String instanceId, String appProfileId) { + return awaitFuture(getAppProfileAsync(instanceId, appProfileId)); + } + + /** + * Asynchronously get the app profile by id. + * + *

Sample code: + * + *

{@code
+   * ApiFuture appProfileFuture = client.getAppProfileAsync("my-instance", "my-app-profile");
+   *
+   * AppProfile appProfile = appProfileFuture.get();
+   * }
+ * + * @see AppProfile + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture getAppProfileAsync(String instanceId, String appProfileId) { + AppProfileName name = AppProfileName.of(projectName.getProject(), instanceId, appProfileId); + + GetAppProfileRequest request = GetAppProfileRequest.newBuilder() .setName(name.toString()) .build(); return ApiFutures.transform( - stub.deleteClusterCallable().futureCall(request), + stub.getAppProfileCallable().futureCall(request), + new ApiFunction() { + @Override + public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) { + return AppProfile.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Lists all app profiles of the specified instance. + * + *

Sample code: + * + *

{@code
+   * List appProfiles = client.listAppProfiles("my-instance");
+   * }
+ * + * @see AppProfile + */ + @SuppressWarnings("WeakerAccess") + public List listAppProfiles(String instanceId) { + return awaitFuture(listAppProfilesAsync(instanceId)); + } + + /** + * Asynchronously lists all app profiles of the specified instance. + * + *

Sample code: + * + *

{@code
+   * ApiFuture> appProfilesFuture = client.listAppProfilesAsync("my-instance");
+   *
+   * List appProfiles = appProfileFuture.get();
+   * }
+ * + * @see AppProfile + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture> listAppProfilesAsync(String instanceId) { + InstanceName instanceName = InstanceName.of(projectName.getProject(), instanceId); + + ListAppProfilesRequest request = ListAppProfilesRequest.newBuilder() + .setParent(instanceName.toString()) + .build(); + + // TODO(igorbernstein2): try to upstream pagination spooling or figure out a way to expose the + // paginated responses while maintaining the wrapper facade. + + // Fetch the first page. + ApiFuture firstPageFuture = ApiFutures.transform( + stub.listAppProfilesPagedCallable().futureCall(request), + new ApiFunction() { + @Override + public ListAppProfilesPage apply(ListAppProfilesPagedResponse response) { + return response.getPage(); + } + }, + MoreExecutors.directExecutor() + ); + + // Fetch the rest of the pages by chaining the futures. + ApiFuture> allProtos = ApiFutures + .transformAsync( + firstPageFuture, + new ApiAsyncFunction>() { + List responseAccumulator = Lists + .newArrayList(); + + @Override + public ApiFuture> apply( + ListAppProfilesPage 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() + ); + + // Wrap 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.AppProfile appProfile : input) { + results.add(AppProfile.fromProto(appProfile)); + } + return results; + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Updates an existing app profile. + * + *

Sample code: + * + *

{@code
+   * AppProfile existingAppProfile = client.getAppProfile("my-instance", "my-app-profile");
+   *
+   * AppProfile updatedAppProfile = client.updateAppProfile(
+   *   UpdateAppProfileRequest.of(existingAppProfile)
+   *     .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"))
+   * );
+   * }
+ * + * @see UpdateAppProfileRequest + */ + @SuppressWarnings("WeakerAccess") + public AppProfile updateAppProfile(UpdateAppProfileRequest request) { + return awaitFuture(updateAppProfileAsync(request)); + } + + /** + * Asynchronously updates an existing app profile. + * + *

Sample code: + * + *

{@code
+   *
+   * ApiFuture existingAppProfileFuture = client.getAppProfileAsync("my-instance", "my-app-profile");
+   *
+   * ApiFuture updatedAppProfileFuture = ApiFutures.transformAsync(
+   *   existingAppProfileFuture,
+   *   new ApiAsyncFunction() {
+   *     @Override
+   *     public ApiFuture apply(AppProfile existingAppProfile) {
+   *       return client.updateAppProfileAsync(
+   *         UpdateAppProfileRequest.of(existingAppProfile)
+   *           .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-other-cluster"))
+   *       );
+   *     }
+   *   },
+   *   MoreExecutors.directExecutor()
+   * );
+   *
+   * ApiFuture appProfile = updatedAppProfileFuture.get();
+   * }
+ * + * @see UpdateAppProfileRequest + */ + @SuppressWarnings("WeakerAccess") + public ApiFuture updateAppProfileAsync(UpdateAppProfileRequest request) { + return ApiFutures.transform( + stub.updateAppProfileOperationCallable().futureCall(request.toProto(projectName)), + new ApiFunction() { + @Override + public AppProfile apply(com.google.bigtable.admin.v2.AppProfile proto) { + return AppProfile.fromProto(proto); + } + }, + MoreExecutors.directExecutor() + ); + } + + /** + * Deletes the specified app profile. + * + *

Sample code: + * + *

{@code
+   * client.deleteAppProfile("my-instance", "my-app-profile");
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public void deleteAppProfile(String instanceId, String appProfileId) { + awaitFuture(deleteAppProfileAsync(instanceId, appProfileId)); + } + + /** + * Asynchronously deletes the specified app profile. + * + *

Sample code: + * + *

{@code
+   * ApiFuture deleteFuture = client.deleteAppProfileAsync("my-instance", "my-app-profile");
+   *
+   * deleteFuture.get();
+   * }
+ */ + @SuppressWarnings("WeakerAccess") + public ApiFuture deleteAppProfileAsync(String instanceId, String appProfileId) { + AppProfileName name = AppProfileName.of(projectName.getProject(), instanceId, appProfileId); + DeleteAppProfileRequest request = DeleteAppProfileRequest.newBuilder() + .setName(name.toString()) + .build(); + + return ApiFutures.transform( + stub.deleteAppProfileCallable().futureCall(request), new ApiFunction() { @Override public Void apply(Empty input) { diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java new file mode 100644 index 000000000000..35c20f7b60e8 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/AppProfile.java @@ -0,0 +1,274 @@ +/* + * 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.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny; +import com.google.bigtable.admin.v2.AppProfileName; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import com.google.common.base.VerifyException; +import javax.annotation.Nonnull; + +/** + *

An application profile, or app profile, stores settings that tell your Cloud Bigtable + * instance how to handle incoming requests from an application. When one of your applications + * connects to a Cloud Bigtable instance, it can specify an app profile, and Cloud Bigtable uses + * that app profile for any requests that the application sends over that connection. + * + *

App profiles affect how your applications communicate with an instance that uses replication. + * As a result, app profiles are especially useful for instances that have 2 clusters. Even if your + * instance has only 1 cluster, you can use a unique app profile for each application that you run, + * or for different components within a single application. + * + * @see For more details. + */ +public final class AppProfile { + private final com.google.bigtable.admin.v2.AppProfile proto; + + /** + * Wraps a protobuf response. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. + */ + @InternalApi + public static AppProfile fromProto(@Nonnull com.google.bigtable.admin.v2.AppProfile proto) { + return new AppProfile(proto); + } + + private AppProfile(@Nonnull com.google.bigtable.admin.v2.AppProfile proto) { + Preconditions.checkNotNull(proto); + Preconditions + .checkArgument(proto.hasSingleClusterRouting() || proto.hasMultiClusterRoutingUseAny(), + "AppProfile must have a routing policy"); + Preconditions.checkArgument(!proto.getName().isEmpty(), "AppProfile must have a name"); + this.proto = proto; + } + + + /** Gets the routing policy for all read/write requests which use this app profile. */ + @SuppressWarnings("WeakerAccess") + public RoutingPolicy getPolicy() { + if (proto.hasMultiClusterRoutingUseAny()) { + return MultiClusterRoutingPolicy.of(); + } else if (proto.hasSingleClusterRouting()) { + return new SingleClusterRoutingPolicy(proto.getSingleClusterRouting()); + } else { + // Should never happen because the constructor verifies that one must exist. + throw new VerifyException(); + } + } + + /** Gets the id of this AppProfile. */ + @SuppressWarnings("WeakerAccess") + public String getId() { + AppProfileName fullName = Verify.verifyNotNull( + AppProfileName.parse(proto.getName()), + "Name can never be null"); + + //noinspection ConstantConditions + return fullName.getAppProfile(); + } + + /** Gets the id of the instance that owns this AppProfile. */ + @SuppressWarnings("WeakerAccess") + public String getInstanceId() { + AppProfileName fullName = Verify.verifyNotNull( + AppProfileName.parse(proto.getName()), + "Name can never be null"); + + //noinspection ConstantConditions + return fullName.getInstance(); + } + + /** Gets long form description of the use case for this AppProfile. */ + @SuppressWarnings("WeakerAccess") + public String getDescription() { + return proto.getDescription(); + } + + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + com.google.bigtable.admin.v2.AppProfile toProto() { + return proto; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AppProfile that = (AppProfile) o; + return Objects.equal(proto, that.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } + + + /** + * Represents the routing for read/write requests. Please check the implementations of this + * interface for more details. + */ + @SuppressWarnings("WeakerAccess") + public interface RoutingPolicy { + } + + /** + * A {@link RoutingPolicy} that routes all requests to a specific cluster. + */ + @SuppressWarnings("WeakerAccess") + public static class SingleClusterRoutingPolicy implements RoutingPolicy { + private final com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting proto; + + /** + * Wraps a protobuf response. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. + */ + @InternalApi + public static SingleClusterRoutingPolicy fromProto( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting proto) { + return new SingleClusterRoutingPolicy(proto); + } + + /** + * Builds a new instance of the routing policy that will send all requests to the specified + * cluster. + * + *

Please note that atomic row transactions will be disabled. + */ + public static SingleClusterRoutingPolicy of(String clusterId) { + return of(clusterId, false); + } + + /** + * Builds a new instance of the routing policy that will send all requests to the specified + * cluster. This variant enables the ability to re-enable single row transactions at the cost of + * consistency. + * + *

Please see the online + * documentation for more details. + */ + public static SingleClusterRoutingPolicy of(String clusterId, boolean allowTransactionWrites) { + return fromProto( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting.newBuilder() + .setClusterId(clusterId) + .setAllowTransactionalWrites(allowTransactionWrites) + .build() + ); + } + + private SingleClusterRoutingPolicy( + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting proto) { + this.proto = proto; + } + + /** Gets the target cluster of this policy. */ + @SuppressWarnings("WeakerAccess") + public String getClusterId() { + return proto.getClusterId(); + } + + /** Checks if transactional writes are enabled. */ + @SuppressWarnings("WeakerAccess") + public boolean getAllowTransactionalWrites() { + return proto.getAllowTransactionalWrites(); + } + + /** + * Wraps a protobuf response. + * + *

This method is considered an internal implementation detail and not meant to be used by + * applications. + */ + @InternalApi + com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting toProto() { + return proto; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SingleClusterRoutingPolicy that = (SingleClusterRoutingPolicy) o; + return Objects.equal(proto, that.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } + } + + /** + * A {@link RoutingPolicy} that tells Cloud Bigtable that it can route each request to any + * available cluster. + */ + public static class MultiClusterRoutingPolicy implements RoutingPolicy { + private static final MultiClusterRoutingUseAny proto = MultiClusterRoutingUseAny + .getDefaultInstance(); + + /** Creates a new instance of {@link MultiClusterRoutingPolicy}. */ + public static MultiClusterRoutingPolicy of() { + return new MultiClusterRoutingPolicy(); + } + + private MultiClusterRoutingPolicy() { + } + + /** + * Creates the request protobuf. This method is considered an internal implementation detail and + * not meant to be used by applications. + */ + @InternalApi + MultiClusterRoutingUseAny toProto() { + return proto; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(proto); + } + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequest.java new file mode 100644 index 000000000000..85141329c153 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequest.java @@ -0,0 +1,102 @@ +/* + * 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.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.RoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import com.google.common.base.Preconditions; +import javax.annotation.Nonnull; + +/** + * Parameters for creating a new Cloud Bigtable app profile. + * + *

An application profile, or app profile, stores settings that tell your Cloud Bigtable + * instance how to handle incoming requests from an application. When one of your applications + * connects to a Cloud Bigtable instance, it can specify an app profile, and Cloud Bigtable uses + * that app profile for any requests that the application sends over that connection. + * + *

Sample code: + * + *

{@code
+ * AppProfile existingAppProfile = ...;
+ * CreateAppProfileRequest appProfileRequest = CreateAppProfileRequest.of("my-instance", "my-new-app-profile")
+ *   .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"));
+ * }
+ * + * @see AppProfile for more details + */ +public final class CreateAppProfileRequest { + private final String instanceId; + private final com.google.bigtable.admin.v2.CreateAppProfileRequest.Builder proto; + + /** Builds a new request to create a new app profile in the specified instance. */ + public static CreateAppProfileRequest of(String instanceId, String appProfileId) { + return new CreateAppProfileRequest(instanceId, appProfileId); + } + + private CreateAppProfileRequest(String instanceId, String appProfileId) { + this.instanceId = instanceId; + this.proto = com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder(); + + proto.setAppProfileId(appProfileId); + proto.getAppProfileBuilder().setDescription(appProfileId); + } + + /** Configures if safety warnings should be disabled. */ + @SuppressWarnings("WeakerAccess") + public CreateAppProfileRequest setIgnoreWarnings(boolean value) { + proto.setIgnoreWarnings(value); + return this; + } + + /** Sets the optional long form description of the use case for the AppProfile. */ + @SuppressWarnings("WeakerAccess") + public CreateAppProfileRequest setDescription(@Nonnull String description) { + proto.getAppProfileBuilder().setDescription(description); + return this; + } + + /** Sets the routing policy for all read/write requests that use this app profile. */ + @SuppressWarnings("WeakerAccess") + public CreateAppProfileRequest setRoutingPolicy(RoutingPolicy routingPolicy) { + Preconditions.checkNotNull(routingPolicy); + + if (routingPolicy instanceof MultiClusterRoutingPolicy) { + proto.getAppProfileBuilder().setMultiClusterRoutingUseAny(((MultiClusterRoutingPolicy)routingPolicy).toProto()); + } else if (routingPolicy instanceof SingleClusterRoutingPolicy) { + proto.getAppProfileBuilder().setSingleClusterRouting(((SingleClusterRoutingPolicy)routingPolicy).toProto()); + } else { + throw new IllegalArgumentException("Unknown policy type: " + routingPolicy); + } + + return this; + } + + /** + * 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.CreateAppProfileRequest toProto(ProjectName projectName) { + InstanceName name = InstanceName.of(projectName.getProject(), instanceId); + + return proto.setParent(name.toString()).build(); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequest.java new file mode 100644 index 000000000000..127861d1e27d --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequest.java @@ -0,0 +1,155 @@ +/* + * 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.bigtable.admin.v2.AppProfileName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.RoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +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 app profile. + * + *

Examples + * + *

{@code
+ * AppProfile existingAppProfile = ...;
+ * UpdateAppProfileRequest appProfileRequest = UpdateAppProfileRequest.of(existingAppProfile)
+ *   .setRoutingPolicy(SingleClusterRoutingPolicy.of("my-cluster"));
+ * }
+ * + * @see AppProfile for more details + */ +public final class UpdateAppProfileRequest { + private final String instanceId; + private final String appProfileId; + + private final com.google.bigtable.admin.v2.UpdateAppProfileRequest.Builder proto; + + /** + * Builds a new update request using an existing AppProfile. + * + *

This variant is recommended over {@link #of(String, String)} because it provides optimistic + * concurrency control using etags. + */ + public static UpdateAppProfileRequest of(@Nonnull AppProfile appProfile) { + return new UpdateAppProfileRequest(appProfile.getInstanceId(), appProfile.getId(), + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder() + .setAppProfile(appProfile.toProto()) + ); + } + + /** Builds a new update request using an existing AppProfile. */ + public static UpdateAppProfileRequest of(@Nonnull String instanceId, + @Nonnull String appProfileId) { + return new UpdateAppProfileRequest(instanceId, appProfileId, + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder()); + } + + private UpdateAppProfileRequest(@Nonnull String instanceId, @Nonnull String appProfileId, + @Nonnull com.google.bigtable.admin.v2.UpdateAppProfileRequest.Builder proto) { + Preconditions.checkNotNull(instanceId, "instanceId must be set"); + Preconditions.checkNotNull(appProfileId, "appProfileId must be set"); + Preconditions.checkNotNull(proto, "proto must be set"); + + this.instanceId = instanceId; + this.appProfileId = appProfileId; + this.proto = proto; + } + + /** Configures if safety warnings should be disabled. */ + @SuppressWarnings("WeakerAccess") + public UpdateAppProfileRequest setIgnoreWarnings(boolean value) { + proto.setIgnoreWarnings(value); + return this; + } + + /** Sets the optional long form description of the use case for the AppProfile. */ + @SuppressWarnings("WeakerAccess") + public UpdateAppProfileRequest setDescription(@Nonnull String description) { + Preconditions.checkNotNull(description); + + proto.getAppProfileBuilder().setDescription(description); + updateFieldMask(com.google.bigtable.admin.v2.AppProfile.DESCRIPTION_FIELD_NUMBER); + return this; + } + + /** Sets the routing policy for all read/write requests that use this app profile. */ + @SuppressWarnings("WeakerAccess") + public UpdateAppProfileRequest setRoutingPolicy(@Nonnull RoutingPolicy routingPolicy) { + Preconditions.checkNotNull(routingPolicy); + + if (routingPolicy instanceof MultiClusterRoutingPolicy) { + proto.getAppProfileBuilder() + .setMultiClusterRoutingUseAny(((MultiClusterRoutingPolicy) routingPolicy).toProto()); + updateFieldMask( + com.google.bigtable.admin.v2.AppProfile.MULTI_CLUSTER_ROUTING_USE_ANY_FIELD_NUMBER); + } else if (routingPolicy instanceof SingleClusterRoutingPolicy) { + proto.getAppProfileBuilder() + .setSingleClusterRouting(((SingleClusterRoutingPolicy) routingPolicy).toProto()); + updateFieldMask(com.google.bigtable.admin.v2.AppProfile.SINGLE_CLUSTER_ROUTING_FIELD_NUMBER); + } else { + throw new IllegalArgumentException("Unknown policy type: " + routingPolicy); + } + + return this; + } + + private void updateFieldMask(int fieldNumber) { + FieldMask newMask = FieldMaskUtil + .fromFieldNumbers(com.google.bigtable.admin.v2.AppProfile.class, fieldNumber); + proto.setUpdateMask(FieldMaskUtil.union(proto.getUpdateMask(), newMask)); + } + + /** + * 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.UpdateAppProfileRequest toProto(ProjectName projectName) { + AppProfileName name = AppProfileName.of(projectName.getProject(), instanceId, appProfileId); + + proto.getAppProfileBuilder().setName(name.toString()); + + return proto.build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UpdateAppProfileRequest that = (UpdateAppProfileRequest) o; + return Objects.equal(instanceId, that.instanceId) && + Objects.equal(appProfileId, that.appProfileId) && + Objects.equal(proto, that.proto); + } + + @Override + public int hashCode() { + return Objects.hashCode(instanceId, appProfileId, proto); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java index 444ba99053ec..487191f19768 100644 --- a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java @@ -26,6 +26,7 @@ import com.google.api.gax.rpc.OperationCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.gax.rpc.testing.FakeOperationSnapshot; +import com.google.bigtable.admin.v2.AppProfileName; import com.google.bigtable.admin.v2.ClusterName; import com.google.bigtable.admin.v2.CreateInstanceMetadata; import com.google.bigtable.admin.v2.Instance.Type; @@ -35,14 +36,21 @@ import com.google.bigtable.admin.v2.StorageType; import com.google.bigtable.admin.v2.UpdateClusterMetadata; import com.google.bigtable.admin.v2.UpdateInstanceMetadata; +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.models.AppProfile; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; import com.google.cloud.bigtable.admin.v2.models.Cluster; +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.Instance; 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.stub.BigtableInstanceAdminStub; +import com.google.common.collect.Lists; import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; import io.grpc.Status.Code; @@ -59,11 +67,14 @@ @RunWith(MockitoJUnitRunner.class) public class BigtableInstanceAdminClientTest { + private static final ProjectName PROJECT_NAME = ProjectName.of("my-project"); private static final InstanceName INSTANCE_NAME = InstanceName.of(PROJECT_NAME.getProject(), "my-instance"); private static final ClusterName CLUSTER_NAME = ClusterName.of(INSTANCE_NAME.getProject(), INSTANCE_NAME.getInstance(), "my-cluster"); + private static final AppProfileName APP_PROFILE_NAME = + AppProfileName.of(INSTANCE_NAME.getProject(), INSTANCE_NAME.getInstance(), "my-cluster"); private BigtableInstanceAdminClient adminClient; @@ -72,20 +83,15 @@ public class BigtableInstanceAdminClientTest { @Mock private OperationCallable mockCreateInstanceCallable; - @Mock private OperationCallable mockUpdateInstanceCallable; - @Mock private UnaryCallable mockGetInstanceCallable; - @Mock private UnaryCallable mockListInstancesCallable; - @Mock private UnaryCallable mockDeleteInstanceCallable; - @Mock private OperationCallable mockCreateClusterCallable; @Mock @@ -97,6 +103,18 @@ public class BigtableInstanceAdminClientTest { @Mock private UnaryCallable mockDeleteClusterCallable; + @Mock + private UnaryCallable mockCreateAppProfileCallable; + @Mock + private UnaryCallable mockGetAppProfileCallable; + @Mock + private UnaryCallable mockListAppProfilesCallable; + @Mock + private OperationCallable mockUpdateAppProfileCallable; + @Mock + private UnaryCallable mockDeleteAppProfileCallable; + + @Before public void setUp() { adminClient = BigtableInstanceAdminClient @@ -105,7 +123,6 @@ public void setUp() { Mockito.when(mockStub.createInstanceOperationCallable()).thenReturn(mockCreateInstanceCallable); Mockito.when(mockStub.partialUpdateInstanceOperationCallable()) .thenReturn(mockUpdateInstanceCallable); - Mockito.when(mockStub.getInstanceCallable()).thenReturn(mockGetInstanceCallable); Mockito.when(mockStub.listInstancesCallable()).thenReturn(mockListInstancesCallable); Mockito.when(mockStub.deleteInstanceCallable()).thenReturn(mockDeleteInstanceCallable); @@ -115,6 +132,13 @@ public void setUp() { Mockito.when(mockStub.listClustersCallable()).thenReturn(mockListClustersCallable); Mockito.when(mockStub.updateClusterOperationCallable()).thenReturn(mockUpdateClusterCallable); Mockito.when(mockStub.deleteClusterCallable()).thenReturn(mockDeleteClusterCallable); + + Mockito.when(mockStub.createAppProfileCallable()).thenReturn(mockCreateAppProfileCallable); + Mockito.when(mockStub.getAppProfileCallable()).thenReturn(mockGetAppProfileCallable); + Mockito.when(mockStub.listAppProfilesPagedCallable()).thenReturn(mockListAppProfilesCallable); + Mockito.when(mockStub.updateAppProfileOperationCallable()) + .thenReturn(mockUpdateAppProfileCallable); + Mockito.when(mockStub.deleteAppProfileCallable()).thenReturn(mockDeleteAppProfileCallable); } @Test @@ -507,6 +531,189 @@ public ApiFuture answer(InvocationOnMock invocationOnMock) { assertThat(wasCalled.get()).isTrue(); } + @Test + public void testCreateAppProfile() { + // Setup + com.google.bigtable.admin.v2.CreateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(INSTANCE_NAME.toString()) + .setAppProfileId(APP_PROFILE_NAME.getAppProfile()) + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .getDefaultInstance()) + ) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .getDefaultInstance()) + .build(); + + Mockito.when(mockCreateAppProfileCallable.futureCall(expectedRequest)).thenReturn( + ApiFutures.immediateFuture(expectedResponse) + ); + + // Execute + AppProfile actualResult = adminClient.createAppProfile( + CreateAppProfileRequest.of(APP_PROFILE_NAME.getInstance(), APP_PROFILE_NAME.getAppProfile()) + .setDescription("my description") + .setRoutingPolicy(MultiClusterRoutingPolicy.of()) + ); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testGetAppProfile() { + // Setup + com.google.bigtable.admin.v2.GetAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.GetAppProfileRequest.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .setDescription("my description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .getDefaultInstance()) + .build(); + + Mockito.when(mockGetAppProfileCallable.futureCall(expectedRequest)) + .thenReturn(ApiFutures.immediateFuture(expectedResponse)); + + // Execute + AppProfile actualResult = adminClient + .getAppProfile(APP_PROFILE_NAME.getInstance(), APP_PROFILE_NAME.getAppProfile()); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testListAppProfiles() { + // Setup + com.google.bigtable.admin.v2.ListAppProfilesRequest expectedRequest = + com.google.bigtable.admin.v2.ListAppProfilesRequest.newBuilder() + .setParent(INSTANCE_NAME.toString()) + .build(); + + // 3 AppProfiles spread across 2 pages + List expectedProtos = Lists.newArrayList(); + for (int i = 0; i < 3; i++) { + expectedProtos.add( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME.toString() + i) + .setDescription("profile" + i) + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .getDefaultInstance()) + .build()); + } + // 2 on the first page + ListAppProfilesPage page0 = Mockito.mock(ListAppProfilesPage.class); + Mockito.when(page0.getValues()).thenReturn(expectedProtos.subList(0, 2)); + Mockito.when(page0.getNextPageToken()).thenReturn("next-page"); + Mockito.when(page0.hasNextPage()).thenReturn(true); + + // 1 on the last page + ListAppProfilesPage page1 = Mockito.mock(ListAppProfilesPage.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 + ListAppProfilesPagedResponse response0 = Mockito.mock(ListAppProfilesPagedResponse.class); + Mockito.when(response0.getPage()).thenReturn(page0); + + Mockito.when(mockListAppProfilesCallable.futureCall(expectedRequest)).thenReturn( + ApiFutures.immediateFuture(response0) + ); + + // Execute + List actualResults = adminClient.listAppProfiles(INSTANCE_NAME.getInstance()); + + // Verify + List expectedResults = Lists.newArrayList(); + for (com.google.bigtable.admin.v2.AppProfile expectedProto : expectedProtos) { + expectedResults.add(AppProfile.fromProto(expectedProto)); + } + + assertThat(actualResults).containsExactlyElementsIn(expectedResults); + } + + @Test + public void testUpdateAppProfile() { + // Setup + com.google.bigtable.admin.v2.UpdateAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder() + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .setDescription("updated description") + ) + .setUpdateMask(FieldMask.newBuilder().addPaths("description")) + .build(); + + com.google.bigtable.admin.v2.AppProfile expectedResponse = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .setDescription("updated description") + .setMultiClusterRoutingUseAny( + com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny + .getDefaultInstance()) + .build(); + + mockOperationResult(mockUpdateAppProfileCallable, expectedRequest, expectedResponse); + + // Execute + AppProfile actualResult = adminClient.updateAppProfile( + UpdateAppProfileRequest.of(APP_PROFILE_NAME.getInstance(), APP_PROFILE_NAME.getAppProfile()) + .setDescription("updated description") + ); + + // Verify + assertThat(actualResult).isEqualTo(AppProfile.fromProto(expectedResponse)); + } + + @Test + public void testDeleteAppProfile() { + // Setup + com.google.bigtable.admin.v2.DeleteAppProfileRequest expectedRequest = + com.google.bigtable.admin.v2.DeleteAppProfileRequest.newBuilder() + .setName(APP_PROFILE_NAME.toString()) + .build(); + + final AtomicBoolean wasCalled = new AtomicBoolean(false); + + Mockito.when(mockDeleteAppProfileCallable.futureCall(expectedRequest)) + .thenAnswer(new Answer>() { + @Override + public ApiFuture answer(InvocationOnMock invocationOnMock) { + wasCalled.set(true); + return ApiFutures.immediateFuture(Empty.getDefaultInstance()); + } + }); + + // Execute + adminClient.deleteAppProfile(APP_PROFILE_NAME.getInstance(), APP_PROFILE_NAME.getAppProfile()); + + // Verify + assertThat(wasCalled.get()).isTrue(); + } + private void mockOperationResult( OperationCallable callable, ReqT request, RespT response) { OperationSnapshot operationSnapshot = FakeOperationSnapshot.newBuilder() diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java new file mode 100644 index 000000000000..33ca709b836b --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/AppProfileTest.java @@ -0,0 +1,84 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting; +import com.google.bigtable.admin.v2.AppProfileName; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AppProfileTest { + private static final com.google.bigtable.admin.v2.AppProfile TEST_PROTO = + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName(AppProfileName.of("my-project", "my-instance", "my-profile").toString()) + .setDescription("my description") + .setSingleClusterRouting( + SingleClusterRouting.newBuilder() + .setClusterId("my-cluster") + .setAllowTransactionalWrites(true) + .build() + + ) + .setEtag("my-etag") + .build(); + + @Test + public void testFromProto() { + AppProfile profile = AppProfile.fromProto(TEST_PROTO); + + assertThat(profile.getInstanceId()).isEqualTo("my-instance"); + assertThat(profile.getId()).isEqualTo("my-profile"); + assertThat(profile.getDescription()).isEqualTo("my description"); + assertThat(profile.getPolicy()).isEqualTo(SingleClusterRoutingPolicy.of("my-cluster", true)); + } + + @Test + public void testNoNameError() { + Exception actualException = null; + + try { + AppProfile.fromProto( + TEST_PROTO.toBuilder() + .setName("") + .build()); + } catch (Exception e) { + actualException = e; + } + + assertThat(actualException).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testNoPolicyError() { + Exception actualException = null; + + try { + AppProfile.fromProto( + TEST_PROTO.toBuilder() + .clearSingleClusterRouting() + .build()); + } catch (Exception e) { + actualException = e; + } + + assertThat(actualException).isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java new file mode 100644 index 000000000000..ea683d62d742 --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/CreateAppProfileRequestTest.java @@ -0,0 +1,79 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny; +import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting; +import com.google.bigtable.admin.v2.InstanceName; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class CreateAppProfileRequestTest { + @Test + public void testToProto() { + CreateAppProfileRequest wrapper = CreateAppProfileRequest.of("my-instance", "my-profile") + .setDescription("my description") + .setRoutingPolicy( + SingleClusterRoutingPolicy.of("my-cluster", true) + ) + .setIgnoreWarnings(true); + + assertThat(wrapper.toProto(ProjectName.of("my-project"))).isEqualTo( + com.google.bigtable.admin.v2.CreateAppProfileRequest.newBuilder() + .setParent(InstanceName.of("my-project", "my-instance").toString()) + .setAppProfileId("my-profile") + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setDescription("my description") + .setSingleClusterRouting( + SingleClusterRouting.newBuilder() + .setClusterId("my-cluster") + .setAllowTransactionalWrites(true) + ) + ) + .setIgnoreWarnings(true) + .build() + ); + } + + @Test + public void testMultiClusterRouting() { + CreateAppProfileRequest wrapper = CreateAppProfileRequest.of("my-instance", "my-profile") + .setRoutingPolicy(MultiClusterRoutingPolicy.of()); + + assertThat(wrapper.toProto(ProjectName.of("my-project")).getAppProfile() + .getMultiClusterRoutingUseAny()) + .isEqualTo(MultiClusterRoutingUseAny.getDefaultInstance()); + } + + @Test + public void testDefaultDescription() { + CreateAppProfileRequest wrapper = CreateAppProfileRequest.of("my-instance", "my-profile") + .setRoutingPolicy(MultiClusterRoutingPolicy.of()); + + assertThat(wrapper.toProto(ProjectName.of("my-project")).getAppProfile().getDescription()) + .isEqualTo( + "my-profile" + ); + } +} diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java new file mode 100644 index 000000000000..60d69ae5813d --- /dev/null +++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/models/UpdateAppProfileRequestTest.java @@ -0,0 +1,84 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import com.google.bigtable.admin.v2.AppProfile.MultiClusterRoutingUseAny; +import com.google.bigtable.admin.v2.AppProfile.SingleClusterRouting; +import com.google.bigtable.admin.v2.ProjectName; +import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import com.google.protobuf.FieldMask; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class UpdateAppProfileRequestTest { + @Test + public void testToProto() { + UpdateAppProfileRequest wrapper = UpdateAppProfileRequest.of("my-instance", "my-profile") + .setDescription("my description") + .setRoutingPolicy( + SingleClusterRoutingPolicy.of("my-cluster", true) + ) + .setIgnoreWarnings(true); + + assertThat(wrapper.toProto(ProjectName.of("my-project"))).isEqualTo( + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder() + .setAppProfile( + com.google.bigtable.admin.v2.AppProfile.newBuilder() + .setName("projects/my-project/instances/my-instance/appProfiles/my-profile") + .setDescription("my description") + .setSingleClusterRouting( + SingleClusterRouting.newBuilder() + .setClusterId("my-cluster") + .setAllowTransactionalWrites(true) + ) + ) + .setIgnoreWarnings(true) + .setUpdateMask( + FieldMask.newBuilder() + .addPaths("description") + .addPaths("single_cluster_routing") + ) + .build() + ); + } + + @Test + public void testUpdateExisting() { + com.google.bigtable.admin.v2.AppProfile existingProto = com.google.bigtable.admin.v2.AppProfile + .newBuilder() + .setName("projects/my-project/instances/my-instance/appProfiles/my-profile") + .setEtag("my-etag") + .setDescription("description") + .setMultiClusterRoutingUseAny(MultiClusterRoutingUseAny.getDefaultInstance()) + .build(); + + AppProfile existingWrapper = AppProfile.fromProto(existingProto); + + UpdateAppProfileRequest updateWrapper = UpdateAppProfileRequest.of(existingWrapper) + .setDescription("new description"); + + assertThat(updateWrapper.toProto(ProjectName.of("my-project"))).isEqualTo( + com.google.bigtable.admin.v2.UpdateAppProfileRequest.newBuilder() + .setAppProfile(existingProto.toBuilder().setDescription("new description")) + .setUpdateMask(FieldMask.newBuilder().addPaths("description")) + .build() + ); + } +}