If the row does not exist, the value will be null. The returned Batcher instance is not
+ * threadsafe, it can only be used from a single thread. This method allows customization of the
+ * underlying RPCs by passing in a {@link com.google.api.gax.grpc.GrpcCallContext}. The same
+ * context will be reused for all batches. This can be used to customize things like per attempt
+ * timeouts.
+ *
+ *
Performance notice: The ReadRows protocol requires that rows are sent in ascending key
+ * order, which means that the keys are processed sequentially on the server-side, so batching
+ * allows improving throughput but not latency. Lower latencies can be achieved by sending smaller
+ * requests concurrently.
+ *
+ *
newBulkReadRowsBatcher(
+ TargetId targetId, @Nullable Filter filter, @Nullable GrpcCallContext ctx) {
+ Query query = Query.create(targetId);
if (filter != null) {
- query.filter(filter);
+ query = query.filter(filter);
}
return stub.newBulkReadRowsBatcher(query, ctx);
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
index 4744d3ef1e..68c66067b1 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
@@ -16,6 +16,9 @@
package com.google.cloud.bigtable.data.v2.internal;
import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
+import com.google.cloud.bigtable.data.v2.models.TableId;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
@@ -30,6 +33,8 @@
public class NameUtil {
private static final Pattern TABLE_PATTERN =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)");
+ private static final Pattern AUTHORIZED_VIEW_PATTERN =
+ Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");
public static String formatInstanceName(@Nonnull String projectId, @Nonnull String instanceId) {
return "projects/" + projectId + "/instances/" + instanceId;
@@ -40,6 +45,14 @@ public static String formatTableName(
return formatInstanceName(projectId, instanceId) + "/tables/" + tableId;
}
+ public static String formatAuthorizedViewName(
+ @Nonnull String projectId,
+ @Nonnull String instanceId,
+ @Nonnull String tableId,
+ @Nonnull String authorizedViewId) {
+ return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + authorizedViewId;
+ }
+
public static String extractTableIdFromTableName(@Nonnull String fullTableName) {
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
if (!matcher.matches()) {
@@ -47,4 +60,59 @@ public static String extractTableIdFromTableName(@Nonnull String fullTableName)
}
return matcher.group(3);
}
+
+ public static String extractTableIdFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return matcher.group(3);
+ }
+
+ public static String extractTableNameFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return formatTableName(matcher.group(1), matcher.group(2), matcher.group(3));
+ }
+
+ public static String extractAuthorizedViewIdFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return matcher.group(4);
+ }
+
+ /** A helper to convert fully qualified tableName and authorizedViewName to a {@link TargetId} */
+ public static TargetId extractTargetId(
+ @Nonnull String tableName, @Nonnull String authorizedViewName) {
+ if (tableName.isEmpty() && authorizedViewName.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Either table name or authorized view name must be specified. Table name: "
+ + tableName
+ + ", authorized view name: "
+ + authorizedViewName);
+ }
+ if (!tableName.isEmpty() && !authorizedViewName.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Table name and authorized view name cannot be specified at the same time. Table name: "
+ + tableName
+ + ", authorized view name: "
+ + authorizedViewName);
+ }
+
+ if (!tableName.isEmpty()) {
+ String tableId = extractTableIdFromTableName(tableName);
+ return TableId.of(tableId);
+ } else {
+ String tableId = extractTableIdFromAuthorizedViewName(authorizedViewName);
+ String authorizedViewId = extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName);
+ return AuthorizedViewId.of(tableId, authorizedViewId);
+ }
+ }
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java
new file mode 100644
index 0000000000..5f64190b5c
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java
@@ -0,0 +1,55 @@
+/*
+ * 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.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.auto.value.AutoValue;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.common.base.Preconditions;
+
+/**
+ * An implementation of a {@link TargetId} for authorized views.
+ *
+ * See {@link com.google.cloud.bigtable.admin.v2.models.AuthorizedView} for more details about an
+ * authorized view.
+ */
+@AutoValue
+public abstract class AuthorizedViewId implements TargetId {
+ /** Constructs a new AuthorizedViewId object from the specified tableId and authorizedViewId. */
+ public static AuthorizedViewId of(String tableId, String authorizedViewId) {
+ Preconditions.checkNotNull(tableId, "table id can't be null.");
+ Preconditions.checkNotNull(authorizedViewId, "authorized view id can't be null.");
+ return new AutoValue_AuthorizedViewId(tableId, authorizedViewId);
+ }
+
+ abstract String getTableId();
+
+ abstract String getAuthorizedViewId();
+
+ @Override
+ @InternalApi
+ public String toResourceName(String projectId, String instanceId) {
+ return NameUtil.formatAuthorizedViewName(
+ projectId, instanceId, getTableId(), getAuthorizedViewId());
+ }
+
+ @Override
+ @InternalApi
+ public boolean scopedForAuthorizedView() {
+ return true;
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
index a269370748..f6a09d0b6d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
@@ -38,20 +38,31 @@
*/
public final class BulkMutation implements Serializable, Cloneable {
private static final long serialVersionUID = 3522061250439399088L;
-
- private final String tableId;
+ private final TargetId targetId;
private transient MutateRowsRequest.Builder builder;
private long mutationCountSum = 0;
+ /** @deprecated Please use {@link BulkMutation#create(TargetId)} instead. */
+ @Deprecated
public static BulkMutation create(String tableId) {
- return new BulkMutation(tableId);
+ return new BulkMutation(TableId.of(tableId));
}
- private BulkMutation(@Nonnull String tableId) {
- Preconditions.checkNotNull(tableId);
+ /**
+ * Creates a new instance of the bulk mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static BulkMutation create(TargetId targetId) {
+ return new BulkMutation(targetId);
+ }
+
+ private BulkMutation(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
- this.tableId = tableId;
+ this.targetId = targetId;
this.builder = MutateRowsRequest.newBuilder();
}
@@ -117,14 +128,15 @@ public int getEntryCount() {
@InternalApi
public MutateRowsRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -140,8 +152,11 @@ public MutateRowsRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
BulkMutation bulkMutation =
- BulkMutation.create(NameUtil.extractTableIdFromTableName(request.getTableName()));
+ BulkMutation.create(NameUtil.extractTargetId(tableName, authorizedViewName));
bulkMutation.builder = request.toBuilder();
return bulkMutation;
@@ -150,7 +165,7 @@ public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
/** Creates a copy of {@link BulkMutation}. */
@Override
public BulkMutation clone() {
- BulkMutation bulkMutation = BulkMutation.create(tableId);
+ BulkMutation bulkMutation = BulkMutation.create(targetId);
bulkMutation.builder = this.builder.clone();
return bulkMutation;
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
index ac4c548942..14841f9f4d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
@@ -33,25 +33,49 @@
public final class ConditionalRowMutation implements Serializable {
private static final long serialVersionUID = -3699904745621909502L;
- private final String tableId;
+ private final TargetId targetId;
private transient CheckAndMutateRowRequest.Builder builder =
CheckAndMutateRowRequest.newBuilder();
- private ConditionalRowMutation(String tableId, ByteString rowKey) {
- this.tableId = tableId;
+ private ConditionalRowMutation(TargetId targetId, ByteString rowKey) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
builder.setRowKey(rowKey);
}
- /** Creates a new instance of the mutation builder. */
+ /** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, String)} instead. */
+ @Deprecated
public static ConditionalRowMutation create(String tableId, String rowKey) {
return create(tableId, ByteString.copyFromUtf8(rowKey));
}
- /** Creates a new instance of the mutation builder. */
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ConditionalRowMutation create(TargetId targetId, String rowKey) {
+ return create(targetId, ByteString.copyFromUtf8(rowKey));
+ }
+
+ /** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, ByteString)} instead. */
+ @Deprecated
public static ConditionalRowMutation create(String tableId, ByteString rowKey) {
Validations.validateTableId(tableId);
- return new ConditionalRowMutation(tableId, rowKey);
+ return new ConditionalRowMutation(TableId.of(tableId), rowKey);
+ }
+
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ConditionalRowMutation create(TargetId targetId, ByteString rowKey) {
+ return new ConditionalRowMutation(targetId, rowKey);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -80,7 +104,8 @@ public ConditionalRowMutation condition(@Nonnull Filter condition) {
Preconditions.checkNotNull(condition);
Preconditions.checkState(
!builder.hasPredicateFilter(),
- "Can only have a single condition, please use a Filters#chain or Filters#interleave filter instead");
+ "Can only have a single condition, please use a Filters#chain or Filters#interleave filter"
+ + " instead");
// TODO: verify that the condition does not use any FILTERS.condition() filters
builder.setPredicateFilter(condition.toProto());
@@ -129,13 +154,16 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
Preconditions.checkState(
!builder.getTrueMutationsList().isEmpty() || !builder.getFalseMutationsList().isEmpty(),
"ConditionalRowMutations must have `then` or `otherwise` mutations.");
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
- return builder
- .setTableName(tableName.toString())
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -146,9 +174,12 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static ConditionalRowMutation fromProto(@Nonnull CheckAndMutateRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
ConditionalRowMutation rowMutation =
- ConditionalRowMutation.create(tableId, request.getRowKey());
+ ConditionalRowMutation.create(
+ NameUtil.extractTargetId(tableName, authorizedViewName), request.getRowKey());
rowMutation.builder = request.toBuilder();
return rowMutation;
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
index c7e69d70d4..1b4cb8d680 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
@@ -47,20 +47,31 @@ public final class Query implements Serializable {
// bigtable can server the largest filter size of 20KB.
private static final int MAX_FILTER_SIZE = 20 * 1024;
- private final String tableId;
+ private final TargetId targetId;
private transient ReadRowsRequest.Builder builder = ReadRowsRequest.newBuilder();
+ /** @deprecated Please use {@link Query#create(TargetId)} instead. */
+ @Deprecated
+ public static Query create(String tableId) {
+ return new Query(TableId.of(tableId));
+ }
+
/**
- * Constructs a new Query object for the specified table id. The table id will be combined with
- * the instance name specified in the {@link
+ * Constructs a new Query object for the given target with targetId. The target id will be
+ * combined with the instance name specified in the {@link
* com.google.cloud.bigtable.data.v2.BigtableDataSettings}.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static Query create(String tableId) {
- return new Query(tableId);
+ public static Query create(TargetId targetId) {
+ return new Query(targetId);
}
- private Query(String tableId) {
- this.tableId = tableId;
+ private Query(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -260,7 +271,8 @@ public List shard(SortedSet splitPoints) {
List shards = Lists.newArrayListWithCapacity(shardedRowSets.size());
for (RowSet rowSet : shardedRowSets) {
- Query queryShard = new Query(tableId);
+ Query queryShard;
+ queryShard = new Query(targetId);
queryShard.builder.mergeFrom(this.builder.build());
queryShard.builder.setRows(rowSet);
shards.add(queryShard);
@@ -303,14 +315,14 @@ public ByteStringRange getBound() {
*/
@InternalApi
public ReadRowsRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -321,15 +333,17 @@ public ReadRowsRequest toProto(RequestContext requestContext) {
*/
public static Query fromProto(@Nonnull ReadRowsRequest request) {
Preconditions.checkArgument(request != null, "ReadRowsRequest must not be null");
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
- Query query = new Query(NameUtil.extractTableIdFromTableName(request.getTableName()));
+ Query query = new Query(NameUtil.extractTargetId(tableName, authorizedViewName));
query.builder = request.toBuilder();
return query;
}
public Query clone() {
- Query query = Query.create(tableId);
+ Query query = Query.create(targetId);
query.builder = this.builder.clone();
return query;
}
@@ -424,7 +438,7 @@ public boolean equals(Object o) {
return false;
}
Query query = (Query) o;
- return Objects.equal(tableId, query.tableId)
+ return Objects.equal(targetId, query.targetId)
&& Objects.equal(builder.getRows(), query.builder.getRows())
&& Objects.equal(builder.getFilter(), query.builder.getFilter())
&& Objects.equal(builder.getRowsLimit(), query.builder.getRowsLimit());
@@ -433,7 +447,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
return Objects.hashCode(
- tableId, builder.getRows(), builder.getFilter(), builder.getRowsLimit());
+ targetId, builder.getRows(), builder.getFilter(), builder.getRowsLimit());
}
@Override
@@ -441,7 +455,7 @@ public String toString() {
ReadRowsRequest request = builder.build();
return MoreObjects.toStringHelper(this)
- .add("tableId", tableId)
+ .add("targetId", targetId)
.add("keys", request.getRows().getRowKeysList())
.add("ranges", request.getRows().getRowRangesList())
.add("filter", request.getFilter())
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
index 5fa483d1bd..554a0268b9 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
@@ -33,25 +33,49 @@
public final class ReadModifyWriteRow implements Serializable {
private static final long serialVersionUID = -8150045424541029193L;
- private final String tableId;
+ private final TargetId targetId;
private transient ReadModifyWriteRowRequest.Builder builder =
ReadModifyWriteRowRequest.newBuilder();
- private ReadModifyWriteRow(@Nonnull String tableId, @Nonnull ByteString key) {
- Preconditions.checkNotNull(tableId, "tableId can't be null.");
+ private ReadModifyWriteRow(TargetId targetId, ByteString key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
Preconditions.checkNotNull(key, "key can't be null.");
- this.tableId = tableId;
+ this.targetId = targetId;
builder.setRowKey(key);
}
- public static ReadModifyWriteRow create(@Nonnull String tableId, @Nonnull String key) {
+ /** @deprecated Please use {@link ReadModifyWriteRow#create(TargetId, String)} instead. */
+ @Deprecated
+ public static ReadModifyWriteRow create(String tableId, String key) {
Preconditions.checkNotNull(key, "key can't be null.");
- return new ReadModifyWriteRow(tableId, ByteString.copyFromUtf8(key));
+ return new ReadModifyWriteRow(TableId.of(tableId), ByteString.copyFromUtf8(key));
}
- public static ReadModifyWriteRow create(@Nonnull String tableId, @Nonnull ByteString key) {
- return new ReadModifyWriteRow(tableId, key);
+ /**
+ * Creates a new instance of the ReadModifyWriteRow for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ReadModifyWriteRow create(TargetId targetId, String key) {
+ return new ReadModifyWriteRow(targetId, ByteString.copyFromUtf8(key));
+ }
+
+ /** @deprecated Please use {@link ReadModifyWriteRow#create(TargetId, ByteString)} instead. */
+ @Deprecated
+ public static ReadModifyWriteRow create(String tableId, ByteString key) {
+ return new ReadModifyWriteRow(TableId.of(tableId), key);
+ }
+
+ /**
+ * Creates a new instance of the ReadModifyWriteRow for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ReadModifyWriteRow create(TargetId targetId, ByteString key) {
+ return new ReadModifyWriteRow(targetId, key);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -129,14 +153,14 @@ public ReadModifyWriteRow increment(
@InternalApi
public ReadModifyWriteRowRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -147,9 +171,12 @@ public ReadModifyWriteRowRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static ReadModifyWriteRow fromProto(@Nonnull ReadModifyWriteRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
- ReadModifyWriteRow row = ReadModifyWriteRow.create(tableId, request.getRowKey());
+ ReadModifyWriteRow row =
+ ReadModifyWriteRow.create(
+ NameUtil.extractTargetId(tableName, authorizedViewName), request.getRowKey());
row.builder = request.toBuilder();
return row;
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
index 940b76702c..4dfe751225 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
@@ -23,6 +23,7 @@
import com.google.cloud.bigtable.data.v2.internal.NameUtil;
import com.google.cloud.bigtable.data.v2.internal.RequestContext;
import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
+import com.google.common.base.Preconditions;
import com.google.protobuf.ByteString;
import java.io.Serializable;
import javax.annotation.Nonnull;
@@ -34,60 +35,102 @@
public final class RowMutation implements MutationApi, Serializable {
private static final long serialVersionUID = 6529002234913236318L;
- private final String tableId;
+ private final TargetId targetId;
private final ByteString key;
private final Mutation mutation;
- private RowMutation(String tableId, ByteString key, Mutation mutation) {
- this.tableId = tableId;
+ private RowMutation(TargetId targetId, ByteString key, Mutation mutation) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
this.key = key;
this.mutation = mutation;
}
- /** Creates a new instance of the mutation builder. */
- public static RowMutation create(@Nonnull String tableId, @Nonnull String key) {
+ /** @deprecated Please use {@link RowMutation#create(TargetId, String)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, String key) {
return create(tableId, ByteString.copyFromUtf8(key));
}
- /** Creates a new instance of the mutation builder. */
- public static RowMutation create(@Nonnull String tableId, @Nonnull ByteString key) {
- return new RowMutation(tableId, key, Mutation.create());
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static RowMutation create(TargetId targetId, String key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ return create(targetId, ByteString.copyFromUtf8(key));
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, ByteString)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, ByteString key) {
+ return new RowMutation(TableId.of(tableId), key, Mutation.create());
}
/**
- * Creates new instance of mutation builder by wrapping existing set of row mutations. The builder
- * will be owned by this RowMutation and should not be used by the caller after this call. This
- * functionality is intended for advanced usage.
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static RowMutation create(TargetId targetId, ByteString key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ return new RowMutation(targetId, key, Mutation.create());
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, String, Mutation)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, String key, Mutation mutation) {
+ return create(tableId, ByteString.copyFromUtf8(key), mutation);
+ }
+
+ /**
+ * Creates new instance of mutation builder for the given target with targetId by wrapping
+ * existing set of row mutations. The builder will be owned by this RowMutation and should not be
+ * used by the caller after this call. This functionality is intended for advanced usage.
*
* Sample code:
*
*
* Mutation mutation = Mutation.create()
* .setCell("[FAMILY_NAME]", "[QUALIFIER]", [TIMESTAMP], "[VALUE]");
- * RowMutation rowMutation = RowMutation.create("[TABLE]", "[ROW_KEY]", mutation);
+ * RowMutation rowMutation = RowMutation.create(TableId.of("[TABLE]"), "[ROW_KEY]", mutation);
*
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static RowMutation create(
- @Nonnull String tableId, @Nonnull String key, @Nonnull Mutation mutation) {
- return create(tableId, ByteString.copyFromUtf8(key), mutation);
+ public static RowMutation create(TargetId targetId, String key, Mutation mutation) {
+ return create(targetId, ByteString.copyFromUtf8(key), mutation);
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, ByteString, Mutation)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, ByteString key, Mutation mutation) {
+ return new RowMutation(TableId.of(tableId), key, mutation);
}
/**
- * Creates new instance of mutation builder by wrapping existing set of row mutations. The builder
- * will be owned by this RowMutation and should not be used by the caller after this call. This
- * functionality is intended for advanced usage.
+ * Creates new instance of mutation builder for the given target with targetId by wrapping
+ * existing set of row mutations. The builder will be owned by this RowMutation and should not be
+ * used by the caller after this call. This functionality is intended for advanced usage.
*
* Sample code:
*
*
* Mutation mutation = Mutation.create()
* .setCell("[FAMILY_NAME]", "[QUALIFIER]", [TIMESTAMP], "[VALUE]");
- * RowMutation rowMutation = RowMutation.create("[TABLE]", [BYTE_STRING_ROW_KEY], mutation);
+ * RowMutation rowMutation = RowMutation.create(TableId.of("[TABLE]"), [BYTE_STRING_ROW_KEY], mutation);
*
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static RowMutation create(
- @Nonnull String tableId, @Nonnull ByteString key, @Nonnull Mutation mutation) {
- return new RowMutation(tableId, key, mutation);
+ public static RowMutation create(TargetId targetId, ByteString key, Mutation mutation) {
+ return new RowMutation(targetId, key, mutation);
}
@Override
@@ -196,13 +239,17 @@ public RowMutation addToCell(
@InternalApi
public MutateRowRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
+ MutateRowRequest.Builder builder = MutateRowRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
- return MutateRowRequest.newBuilder()
+ return builder
.setAppProfileId(requestContext.getAppProfileId())
- .setTableName(tableName)
.setRowKey(key)
.addAllMutations(mutation.getMutations())
.build();
@@ -214,13 +261,17 @@ public MutateRowRequest toProto(RequestContext requestContext) {
*/
@InternalApi
public MutateRowsRequest toBulkProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
+ MutateRowsRequest.Builder builder = MutateRowsRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
- return MutateRowsRequest.newBuilder()
+ return builder
.setAppProfileId(requestContext.getAppProfileId())
- .setTableName(tableName)
.addEntries(
Entry.newBuilder().setRowKey(key).addAllMutations(mutation.getMutations()).build())
.build();
@@ -239,9 +290,12 @@ public MutateRowsRequest toBulkProto(RequestContext requestContext) {
*/
@BetaApi
public static RowMutation fromProto(@Nonnull MutateRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
return RowMutation.create(
- tableId, request.getRowKey(), Mutation.fromProto(request.getMutationsList()));
+ NameUtil.extractTargetId(tableName, authorizedViewName),
+ request.getRowKey(),
+ Mutation.fromProto(request.getMutationsList()));
}
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java
new file mode 100644
index 0000000000..08d9a3ca23
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+
+/** Wraps a {@link com.google.bigtable.v2.SampleRowKeysRequest}. */
+public final class SampleRowKeysRequest implements Serializable {
+ private final TargetId targetId;
+
+ private SampleRowKeysRequest(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ this.targetId = targetId;
+ }
+
+ /** Creates a new instance of the sample row keys builder for the given target with targetId */
+ public static SampleRowKeysRequest create(TargetId targetId) {
+ return new SampleRowKeysRequest(targetId);
+ }
+
+ @InternalApi
+ public com.google.bigtable.v2.SampleRowKeysRequest toProto(RequestContext requestContext) {
+ com.google.bigtable.v2.SampleRowKeysRequest.Builder builder =
+ com.google.bigtable.v2.SampleRowKeysRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
+ }
+
+ /**
+ * Wraps the protobuf {@link com.google.bigtable.v2.SampleRowKeysRequest}.
+ *
+ * WARNING: Please note that the project id & instance id in the table/authorized view name
+ * will be overwritten by the configuration in the BigtableDataClient.
+ */
+ @InternalApi
+ public static SampleRowKeysRequest fromProto(
+ @Nonnull com.google.bigtable.v2.SampleRowKeysRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
+ SampleRowKeysRequest sampleRowKeysRequest =
+ SampleRowKeysRequest.create(NameUtil.extractTargetId(tableName, authorizedViewName));
+
+ return sampleRowKeysRequest;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SampleRowKeysRequest sampleRowKeysRequest = (SampleRowKeysRequest) o;
+ return Objects.equal(targetId, sampleRowKeysRequest.targetId);
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java
new file mode 100644
index 0000000000..15b2cd9d95
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java
@@ -0,0 +1,47 @@
+/*
+ * 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.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.auto.value.AutoValue;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.common.base.Preconditions;
+
+/** An implementation of a {@link TargetId} for tables. */
+@AutoValue
+public abstract class TableId implements TargetId {
+
+ /** Constructs a new TableId object for the specified table id. */
+ public static TableId of(String tableId) {
+ Preconditions.checkNotNull(tableId, "table id can't be null.");
+ return new AutoValue_TableId(tableId);
+ }
+
+ abstract String getTableId();
+
+ @Override
+ @InternalApi
+ public String toResourceName(String projectId, String instanceId) {
+ return NameUtil.formatTableName(projectId, instanceId, getTableId());
+ }
+
+ @Override
+ @InternalApi
+ public boolean scopedForAuthorizedView() {
+ return false;
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java
new file mode 100644
index 0000000000..ae5be23598
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java
@@ -0,0 +1,47 @@
+/*
+ * 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.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.api.core.InternalExtensionOnly;
+import java.io.Serializable;
+
+/**
+ * TargetId defines the scope a data operation can be applied to.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+@InternalExtensionOnly
+public interface TargetId extends Serializable {
+ /**
+ * Combines the table or authorized view id with the projectId and instanceId to form the actual
+ * resource name in the request protobuf.
+ *
+ *
This method is considered an internal implementation detail and not meant to be used by
+ * applications.
+ */
+ @InternalApi
+ String toResourceName(String projectId, String instanceId);
+
+ /**
+ * Returns true if this TargetId object represents id for an authorized view (rather than a
+ * table).
+ */
+ @InternalApi
+ boolean scopedForAuthorizedView();
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
index 9c472b6c02..ec15c4131a 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
@@ -66,10 +66,10 @@
import com.google.bigtable.v2.ReadRowsRequest;
import com.google.bigtable.v2.ReadRowsResponse;
import com.google.bigtable.v2.RowRange;
-import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.SampleRowKeysResponse;
import com.google.cloud.bigtable.Version;
import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
import com.google.cloud.bigtable.data.v2.internal.RequestContext;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.ChangeStreamMutation;
@@ -87,6 +87,8 @@
import com.google.cloud.bigtable.data.v2.models.RowAdapter;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import com.google.cloud.bigtable.data.v2.stub.changestream.ChangeStreamRecordMergingCallable;
import com.google.cloud.bigtable.data.v2.stub.changestream.GenerateInitialChangeStreamPartitionsUserCallable;
import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamResumptionStrategy;
@@ -166,6 +168,8 @@ public class EnhancedBigtableStub implements AutoCloseable {
private final UnaryCallable readRowCallable;
private final UnaryCallable> bulkReadRowsCallable;
private final UnaryCallable> sampleRowKeysCallable;
+ private final UnaryCallable>
+ sampleRowKeysCallableWithRequest;
private final UnaryCallable mutateRowCallable;
private final UnaryCallable bulkMutateRowsCallable;
private final UnaryCallable externalBulkMutateRowsCallable;
@@ -370,6 +374,7 @@ public EnhancedBigtableStub(
readRowCallable = createReadRowCallable(new DefaultRowAdapter());
bulkReadRowsCallable = createBulkReadRowsCallable(new DefaultRowAdapter());
sampleRowKeysCallable = createSampleRowKeysCallable();
+ sampleRowKeysCallableWithRequest = createSampleRowKeysCallableWithRequest();
mutateRowCallable = createMutateRowCallable();
bulkMutateRowsCallable = createMutateRowsBaseCallable();
externalBulkMutateRowsCallable =
@@ -498,9 +503,17 @@ private ServerStreamingCallable createReadRo
new RequestParamsExtractor() {
@Override
public Map extract(ReadRowsRequest readRowsRequest) {
+ String tableName = readRowsRequest.getTableName();
+ String authorizedViewName = readRowsRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", readRowsRequest.getTableName(),
- "app_profile_id", readRowsRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ readRowsRequest.getAppProfileId());
}
})
.build(),
@@ -583,6 +596,57 @@ private UnaryCallable> createBulkReadRowsCallable(
return traced.withDefaultCallContext(clientContext.getDefaultCallContext());
}
+ /**
+ * Helper function that should only be used by createSampleRowKeysCallable() and
+ * createSampleRowKeysWithRequestCallable().
+ */
+ private UnaryCallable>
+ createSampleRowKeysBaseCallable() {
+ ServerStreamingCallable
+ base =
+ GrpcRawCallableFactory.createServerStreamingCallable(
+ GrpcCallSettings
+ .
+ newBuilder()
+ .setMethodDescriptor(BigtableGrpc.getSampleRowKeysMethod())
+ .setParamsExtractor(
+ new RequestParamsExtractor() {
+ @Override
+ public Map extract(
+ com.google.bigtable.v2.SampleRowKeysRequest sampleRowKeysRequest) {
+ String tableName = sampleRowKeysRequest.getTableName();
+ String authorizedViewName =
+ sampleRowKeysRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(
+ authorizedViewName);
+ }
+ return ImmutableMap.of(
+ "table_name",
+ tableName,
+ "app_profile_id",
+ sampleRowKeysRequest.getAppProfileId());
+ }
+ })
+ .build(),
+ settings.sampleRowKeysSettings().getRetryableCodes());
+
+ UnaryCallable>
+ spoolable = base.all();
+
+ UnaryCallable>
+ withStatsHeaders = new StatsHeadersUnaryCallable<>(spoolable);
+
+ UnaryCallable>
+ withBigtableTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders);
+
+ UnaryCallable>
+ retryable = withRetries(withBigtableTracer, settings.sampleRowKeysSettings());
+
+ return retryable;
+ }
+
/**
* Creates a callable chain to handle SampleRowKeys RPcs. The chain will:
*
@@ -598,36 +662,33 @@ private UnaryCallable> createBulkReadRowsCallable(
private UnaryCallable> createSampleRowKeysCallable() {
String methodName = "SampleRowKeys";
- ServerStreamingCallable base =
- GrpcRawCallableFactory.createServerStreamingCallable(
- GrpcCallSettings.newBuilder()
- .setMethodDescriptor(BigtableGrpc.getSampleRowKeysMethod())
- .setParamsExtractor(
- new RequestParamsExtractor() {
- @Override
- public Map extract(
- SampleRowKeysRequest sampleRowKeysRequest) {
- return ImmutableMap.of(
- "table_name", sampleRowKeysRequest.getTableName(),
- "app_profile_id", sampleRowKeysRequest.getAppProfileId());
- }
- })
- .build(),
- settings.sampleRowKeysSettings().getRetryableCodes());
-
- UnaryCallable> spoolable = base.all();
-
- UnaryCallable> withStatsHeaders =
- new StatsHeadersUnaryCallable<>(spoolable);
-
- UnaryCallable> withBigtableTracer =
- new BigtableTracerUnaryCallable<>(withStatsHeaders);
+ UnaryCallable>
+ baseCallable = createSampleRowKeysBaseCallable();
+ return createUserFacingUnaryCallable(
+ methodName, new SampleRowKeysCallable(baseCallable, requestContext));
+ }
- UnaryCallable> retryable =
- withRetries(withBigtableTracer, settings.sampleRowKeysSettings());
+ /**
+ * Creates a callable chain to handle SampleRowKeys RPcs. The chain will:
+ *
+ *
+ * - Convert a {@link SampleRowKeysRequest} to a {@link
+ * com.google.bigtable.v2.SampleRowKeysRequest}.
+ *
- Dispatch the request to the GAPIC's {@link BigtableStub#sampleRowKeysCallable()}.
+ *
- Spool responses into a list.
+ *
- Retry on failure.
+ *
- Convert the responses into {@link KeyOffset}s.
+ *
- Add tracing & metrics.
+ *
+ */
+ private UnaryCallable>
+ createSampleRowKeysCallableWithRequest() {
+ String methodName = "SampleRowKeys";
+ UnaryCallable>
+ baseCallable = createSampleRowKeysBaseCallable();
return createUserFacingUnaryCallable(
- methodName, new SampleRowKeysCallable(retryable, requestContext));
+ methodName, new SampleRowKeysCallableWithRequest(baseCallable, requestContext));
}
/**
@@ -648,9 +709,17 @@ private UnaryCallable createMutateRowCallable() {
new RequestParamsExtractor() {
@Override
public Map extract(MutateRowRequest mutateRowRequest) {
+ String tableName = mutateRowRequest.getTableName();
+ String authorizedViewName = mutateRowRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", mutateRowRequest.getTableName(),
- "app_profile_id", mutateRowRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ mutateRowRequest.getAppProfileId());
}
})
.build(),
@@ -696,9 +765,17 @@ private UnaryCallable createMutateRowsBas
new RequestParamsExtractor() {
@Override
public Map extract(MutateRowsRequest mutateRowsRequest) {
+ String tableName = mutateRowsRequest.getTableName();
+ String authorizedViewName = mutateRowsRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", mutateRowsRequest.getTableName(),
- "app_profile_id", mutateRowsRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ mutateRowsRequest.getAppProfileId());
}
})
.build(),
@@ -809,6 +886,37 @@ public Batcher newMutateRowsBatcher(
MoreObjects.firstNonNull(ctx, clientContext.getDefaultCallContext()));
}
+ /**
+ * Creates a {@link BatcherImpl} to handle {@link MutateRowsRequest.Entry} mutations. This is
+ * meant to be used for automatic batching with flow control.
+ *
+ *
+ * - Uses {@link MutateRowsBatchingDescriptor} to spool the {@link RowMutationEntry} mutations
+ * and send them out as {@link BulkMutation}.
+ *
- Uses {@link #bulkMutateRowsCallable()} to perform RPC.
+ *
- Batching thresholds can be configured from {@link
+ * EnhancedBigtableStubSettings#bulkMutateRowsSettings()}.
+ *
- Process the response and schedule retries. At the end of each attempt, entries that have
+ * been applied, are filtered from the next attempt. Also, any entries that failed with a
+ * nontransient error, are filtered from the next attempt. This will continue until there
+ * are no more entries or there are no more retry attempts left.
+ *
- Wrap batch failures in a {@link
+ * com.google.cloud.bigtable.data.v2.models.MutateRowsException}.
+ *
- Split the responses using {@link MutateRowsBatchingDescriptor}.
+ *
+ */
+ public Batcher newMutateRowsBatcher(
+ TargetId targetId, @Nullable GrpcCallContext ctx) {
+ return new BatcherImpl<>(
+ settings.bulkMutateRowsSettings().getBatchingDescriptor(),
+ bulkMutateRowsCallable,
+ BulkMutation.create(targetId),
+ settings.bulkMutateRowsSettings().getBatchingSettings(),
+ clientContext.getExecutor(),
+ bulkMutationFlowController,
+ MoreObjects.firstNonNull(ctx, clientContext.getDefaultCallContext()));
+ }
+
/**
* Creates a {@link BatcherImpl} to handle {@link Query#rowKey(String)}. This is meant for bulk
* read with flow control.
@@ -857,9 +965,18 @@ private UnaryCallable createCheckAndMutateRowCa
@Override
public Map extract(
CheckAndMutateRowRequest checkAndMutateRowRequest) {
+ String tableName = checkAndMutateRowRequest.getTableName();
+ String authorizedViewName =
+ checkAndMutateRowRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", checkAndMutateRowRequest.getTableName(),
- "app_profile_id", checkAndMutateRowRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ checkAndMutateRowRequest.getAppProfileId());
}
})
.build(),
@@ -897,9 +1014,14 @@ private UnaryCallable createReadModifyWriteRowCallable(
new RequestParamsExtractor() {
@Override
public Map extract(ReadModifyWriteRowRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", request.getTableName(),
- "app_profile_id", request.getAppProfileId());
+ "table_name", tableName, "app_profile_id", request.getAppProfileId());
}
})
.build(),
@@ -1147,6 +1269,7 @@ private ServerStreamingCallable withR
}
return retrying;
}
+
//
//
@@ -1164,6 +1287,10 @@ public UnaryCallable> sampleRowKeysCallable() {
return sampleRowKeysCallable;
}
+ public UnaryCallable> sampleRowKeysCallableWithRequest() {
+ return sampleRowKeysCallableWithRequest;
+ }
+
public UnaryCallable mutateRowCallable() {
return mutateRowCallable;
}
@@ -1212,6 +1339,7 @@ public UnaryCallable readModifyWriteRowCallable() {
UnaryCallable pingAndWarmCallable() {
return pingAndWarmCallable;
}
+
//
private SpanName getSpanName(String methodName) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java
new file mode 100644
index 0000000000..034a4048d0
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.data.v2.stub;
+
+import com.google.api.core.ApiFunction;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.UnaryCallable;
+import com.google.bigtable.v2.SampleRowKeysResponse;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import com.google.cloud.bigtable.data.v2.models.KeyOffset;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.List;
+
+/** Simple wrapper for SampleRowKeys to wrap the request and response protobufs. */
+class SampleRowKeysCallableWithRequest
+ extends UnaryCallable> {
+ private final RequestContext requestContext;
+ private final UnaryCallable<
+ com.google.bigtable.v2.SampleRowKeysRequest, List>
+ inner;
+
+ SampleRowKeysCallableWithRequest(
+ UnaryCallable> inner,
+ RequestContext requestContext) {
+
+ this.requestContext = requestContext;
+ this.inner = inner;
+ }
+
+ @Override
+ public ApiFuture> futureCall(
+ SampleRowKeysRequest request, ApiCallContext context) {
+ ApiFuture> rawResponse =
+ inner.futureCall(request.toProto(requestContext), context);
+
+ return ApiFutures.transform(
+ rawResponse,
+ new ApiFunction, List>() {
+ @Override
+ public List apply(List rawResponse) {
+ return convert(rawResponse);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ private static List convert(List rawResponse) {
+ ImmutableList.Builder results = ImmutableList.builder();
+
+ for (SampleRowKeysResponse element : rawResponse) {
+ results.add(KeyOffset.create(element.getRowKey(), element.getOffsetBytes()));
+ }
+
+ return results.build();
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
index 8baf6a15f4..4c3fd7a42d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
@@ -22,6 +22,7 @@
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.StatusCode.Code;
+import com.google.bigtable.v2.AuthorizedViewName;
import com.google.bigtable.v2.CheckAndMutateRowRequest;
import com.google.bigtable.v2.MutateRowRequest;
import com.google.bigtable.v2.MutateRowsRequest;
@@ -30,7 +31,6 @@
import com.google.bigtable.v2.ResponseParams;
import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.TableName;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.CallOptions;
@@ -108,20 +108,33 @@ static TagValue extractStatusFromFuture(Future> future) {
static String extractTableId(Object request) {
String tableName = null;
+ String authorizedViewName = null;
if (request instanceof ReadRowsRequest) {
tableName = ((ReadRowsRequest) request).getTableName();
+ authorizedViewName = ((ReadRowsRequest) request).getAuthorizedViewName();
} else if (request instanceof MutateRowsRequest) {
tableName = ((MutateRowsRequest) request).getTableName();
+ authorizedViewName = ((MutateRowsRequest) request).getAuthorizedViewName();
} else if (request instanceof MutateRowRequest) {
tableName = ((MutateRowRequest) request).getTableName();
+ authorizedViewName = ((MutateRowRequest) request).getAuthorizedViewName();
} else if (request instanceof SampleRowKeysRequest) {
tableName = ((SampleRowKeysRequest) request).getTableName();
+ authorizedViewName = ((SampleRowKeysRequest) request).getAuthorizedViewName();
} else if (request instanceof CheckAndMutateRowRequest) {
tableName = ((CheckAndMutateRowRequest) request).getTableName();
+ authorizedViewName = ((CheckAndMutateRowRequest) request).getAuthorizedViewName();
} else if (request instanceof ReadModifyWriteRowRequest) {
tableName = ((ReadModifyWriteRowRequest) request).getTableName();
+ authorizedViewName = ((ReadModifyWriteRowRequest) request).getAuthorizedViewName();
+ }
+ if (tableName == null && authorizedViewName == null) return "undefined";
+ if (tableName.isEmpty() && authorizedViewName.isEmpty()) return "undefined";
+ if (!tableName.isEmpty()) {
+ return TableName.parse(tableName).getTable();
+ } else {
+ return AuthorizedViewName.parse(authorizedViewName).getTable();
}
- return !Strings.isNullOrEmpty(tableName) ? TableName.parse(tableName).getTable() : "undefined";
}
/**
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 cb2325d1a0..7622ce5dfa 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
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
+import com.google.cloud.bigtable.data.v2.models.TableId;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -68,4 +70,55 @@ public void extractAuthorizedViewIdFromAuthorizedViewNameTest() {
exception.expect(IllegalArgumentException.class);
NameUtil.extractAuthorizedViewIdFromAuthorizedViewName("bad-format");
}
+
+ @Test
+ public void extractTableIdFromAuthorizedViewNameTest() {
+ String testAuthorizedViewName =
+ "projects/my-project/instances/my-instance/tables/my-table/authorizedViews/my-authorized-view";
+
+ assertThat(
+ com.google.cloud.bigtable.data.v2.internal.NameUtil
+ .extractTableIdFromAuthorizedViewName(testAuthorizedViewName))
+ .isEqualTo("my-table");
+
+ exception.expect(IllegalArgumentException.class);
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTableIdFromAuthorizedViewName(
+ "bad-format");
+ }
+
+ @Test
+ public void extractTableNameFromAuthorizedViewNameTest() {
+ String testAuthorizedViewName =
+ "projects/my-project/instances/my-instance/tables/my-table/authorizedViews/my-authorized-view";
+
+ assertThat(
+ com.google.cloud.bigtable.data.v2.internal.NameUtil
+ .extractTableNameFromAuthorizedViewName(testAuthorizedViewName))
+ .isEqualTo("projects/my-project/instances/my-instance/tables/my-table");
+
+ exception.expect(IllegalArgumentException.class);
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTableNameFromAuthorizedViewName(
+ "bad-format");
+ }
+
+ @Test
+ public void testExtractTargetId() {
+ String testTableName = "projects/my-project/instances/my-instance/tables/my-table";
+ String testAuthorizedViewName =
+ "projects/my-project/instances/my-instance/tables/my-table/authorizedViews/my-authorized-view";
+ assertThat(
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTargetId(testTableName, ""))
+ .isEqualTo(TableId.of("my-table"));
+ assertThat(
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTargetId(
+ "", testAuthorizedViewName))
+ .isEqualTo(AuthorizedViewId.of("my-table", "my-authorized-view"));
+
+ exception.expect(IllegalArgumentException.class);
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTargetId("", "");
+
+ exception.expect(IllegalArgumentException.class);
+ com.google.cloud.bigtable.data.v2.internal.NameUtil.extractTargetId(
+ testTableName, testAuthorizedViewName);
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientTests.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientTests.java
index f4f23085a2..880744bc18 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientTests.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientTests.java
@@ -24,6 +24,7 @@
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.ServerStreamingCallable;
import com.google.api.gax.rpc.UnaryCallable;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.ChangeStreamRecord;
import com.google.cloud.bigtable.data.v2.models.ConditionalRowMutation;
@@ -38,6 +39,9 @@
import com.google.cloud.bigtable.data.v2.models.RowCell;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TableId;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
@@ -75,6 +79,10 @@ public class BigtableDataClientTests {
@Mock private UnaryCallable mockReadRowCallable;
@Mock private UnaryCallable> mockSampleRowKeysCallable;
+
+ @Mock
+ private UnaryCallable> mockSampleRowKeysCallableWithRequest;
+
@Mock private UnaryCallable mockMutateRowCallable;
@Mock private UnaryCallable mockCheckAndMutateRowCallable;
@Mock private UnaryCallable mockReadModifyWriteRowCallable;
@@ -130,6 +138,37 @@ public void existsTest() {
Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
}
+ @Test
+ public void existsOnAuthorizedViewTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ Query expectedQuery =
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(
+ FILTERS
+ .chain()
+ .filter(FILTERS.limit().cellsPerRow(1))
+ .filter(FILTERS.value().strip()));
+ Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+ Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
+ .thenReturn(ApiFutures.immediateFuture(row))
+ .thenReturn(ApiFutures.immediateFuture(null));
+
+ boolean result =
+ bigtableDataClient.exists(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key");
+ boolean anotherResult =
+ bigtableDataClient.exists(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"));
+
+ assertThat(result).isTrue();
+ assertThat(anotherResult).isFalse();
+
+ Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
+ }
+
@Test
public void existsAsyncTest() throws Exception {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -158,6 +197,38 @@ public void existsAsyncTest() throws Exception {
Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
}
+ @Test
+ public void existsOnAuthorizedViewAsyncTest() throws Exception {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ Query expectedQuery =
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(
+ FILTERS
+ .chain()
+ .filter(FILTERS.limit().cellsPerRow(1))
+ .filter(FILTERS.value().strip()));
+ Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+
+ Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
+ .thenReturn(ApiFutures.immediateFuture(row))
+ .thenReturn(ApiFutures.immediateFuture(null));
+
+ ApiFuture result =
+ bigtableDataClient.existsAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"));
+ assertThat(result.get()).isTrue();
+
+ ApiFuture anotherResult =
+ bigtableDataClient.existsAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key");
+ assertThat(anotherResult.get()).isFalse();
+
+ Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
+ }
+
@Test
public void proxyReadRowsCallableTest() {
Mockito.when(mockStub.readRowsCallable()).thenReturn(mockReadRowsCallable);
@@ -188,6 +259,19 @@ public void proxyReadRowAsyncTest() {
.futureCall(Query.create("fake-table").rowKey("fake-row-key"));
}
+ @Test
+ public void proxyReadRowOnAuthorizedViewAsyncTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ bigtableDataClient.readRowAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"));
+ Mockito.verify(mockReadRowCallable)
+ .futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key"));
+ }
+
@Test
public void proxyReadRowStrAsyncTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -197,6 +281,18 @@ public void proxyReadRowStrAsyncTest() {
.futureCall(Query.create("fake-table").rowKey("fake-row-key"));
}
+ @Test
+ public void proxyReadRowOnAuthorizedViewStrAsyncTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ bigtableDataClient.readRowAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key");
+ Mockito.verify(mockReadRowCallable)
+ .futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key"));
+ }
+
@Test
public void readRowFilterAsyncTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -213,6 +309,28 @@ public void readRowFilterAsyncTest() {
.futureCall(Query.create("fake-table").rowKey("fake-row-key").filter(filter));
}
+ @Test
+ public void readRowOnAuthorizedViewFilterAsyncTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ // Build the filter expression
+ Filter filter =
+ FILTERS
+ .chain()
+ .filter(FILTERS.qualifier().regex("prefix.*"))
+ .filter(FILTERS.limit().cellsPerRow(10));
+ bigtableDataClient.readRowAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"),
+ filter);
+
+ Mockito.verify(mockReadRowCallable)
+ .futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(filter));
+ }
+
@Test
public void readRowFilterStrAsyncTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -229,6 +347,26 @@ public void readRowFilterStrAsyncTest() {
.futureCall(Query.create("fake-table").rowKey("fake-row-key").filter(filter));
}
+ @Test
+ public void readRowOnAuthorizedViewFilterStrAsyncTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ // Build the filter expression
+ Filter filter =
+ FILTERS
+ .chain()
+ .filter(FILTERS.qualifier().regex("prefix.*"))
+ .filter(FILTERS.limit().cellsPerRow(10));
+ bigtableDataClient.readRowAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key", filter);
+
+ Mockito.verify(mockReadRowCallable)
+ .futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(filter));
+ }
+
@Test
public void readRowTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -244,6 +382,26 @@ public void readRowTest() {
assertThat(actualRow).isEqualTo(expectedRow);
}
+ @Test
+ public void readRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ Row expectedRow =
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+ Mockito.when(
+ mockReadRowCallable.futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")))
+ .thenReturn(ApiFutures.immediateFuture(expectedRow));
+
+ Row actualRow =
+ bigtableDataClient.readRow(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"));
+
+ assertThat(actualRow).isEqualTo(expectedRow);
+ }
+
@Test
public void readRowStrTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -258,6 +416,25 @@ public void readRowStrTest() {
assertThat(actualRow).isEqualTo(expectedRow);
}
+ @Test
+ public void readRowOnAuthorizedViewStrTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ Row expectedRow =
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+ Mockito.when(
+ mockReadRowCallable.futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")))
+ .thenReturn(ApiFutures.immediateFuture(expectedRow));
+
+ Row actualRow =
+ bigtableDataClient.readRow(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key");
+
+ assertThat(actualRow).isEqualTo(expectedRow);
+ }
+
@Test
public void readRowFilterTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -282,6 +459,35 @@ public void readRowFilterTest() {
assertThat(actualRow).isEqualTo(expectedRow);
}
+ @Test
+ public void readRowOnAuthorizedViewFilterTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ // Build the filter expression
+ Filter filter =
+ FILTERS
+ .chain()
+ .filter(FILTERS.qualifier().regex("prefix.*"))
+ .filter(FILTERS.limit().cellsPerRow(10));
+
+ Row expectedRow =
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+ Mockito.when(
+ mockReadRowCallable.futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(filter)))
+ .thenReturn(ApiFutures.immediateFuture(expectedRow));
+
+ Row actualRow =
+ bigtableDataClient.readRow(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ ByteString.copyFromUtf8("fake-row-key"),
+ filter);
+
+ assertThat(actualRow).isEqualTo(expectedRow);
+ }
+
@Test
public void readRowStrFilterTest() {
Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
@@ -304,6 +510,32 @@ public void readRowStrFilterTest() {
assertThat(actualRow).isEqualTo(expectedRow);
}
+ @Test
+ public void readRowOnAuthorizedViewStrFilterTest() {
+ Mockito.when(mockStub.readRowCallable()).thenReturn(mockReadRowCallable);
+
+ // Build the filter expression
+ Filter filter =
+ FILTERS
+ .chain()
+ .filter(FILTERS.qualifier().regex("prefix.*"))
+ .filter(FILTERS.limit().cellsPerRow(10));
+ Row expectedRow =
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.of());
+ Mockito.when(
+ mockReadRowCallable.futureCall(
+ Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .rowKey("fake-row-key")
+ .filter(filter)))
+ .thenReturn(ApiFutures.immediateFuture(expectedRow));
+
+ Row actualRow =
+ bigtableDataClient.readRow(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-row-key", filter);
+
+ assertThat(actualRow).isEqualTo(expectedRow);
+ }
+
@Test
public void proxyReadRowsSyncTest() {
Mockito.when(mockStub.readRowsCallable()).thenReturn(mockReadRowsCallable);
@@ -314,6 +546,16 @@ public void proxyReadRowsSyncTest() {
Mockito.verify(mockReadRowsCallable).call(query);
}
+ @Test
+ public void proxyReadRowsOnAuthorizedViewSyncTest() {
+ Mockito.when(mockStub.readRowsCallable()).thenReturn(mockReadRowsCallable);
+
+ Query query = Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ bigtableDataClient.readRows(query);
+
+ Mockito.verify(mockReadRowsCallable).call(query);
+ }
+
@Test
public void proxyReadRowsAsyncTest() {
Mockito.when(mockStub.readRowsCallable()).thenReturn(mockReadRowsCallable);
@@ -326,6 +568,18 @@ public void proxyReadRowsAsyncTest() {
Mockito.verify(mockReadRowsCallable).call(query, mockObserver);
}
+ @Test
+ public void proxyReadRowsOnAuthorizedViewAsyncTest() {
+ Mockito.when(mockStub.readRowsCallable()).thenReturn(mockReadRowsCallable);
+
+ Query query = Query.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ @SuppressWarnings("unchecked")
+ ResponseObserver mockObserver = Mockito.mock(ResponseObserver.class);
+ bigtableDataClient.readRowsAsync(query, mockObserver);
+
+ Mockito.verify(mockReadRowsCallable).call(query, mockObserver);
+ }
+
@Test
public void proxyGenerateInitialChangeStreamPartitionsSyncTest() {
Mockito.when(mockStub.generateInitialChangeStreamPartitionsCallable())
@@ -381,20 +635,53 @@ public void proxySampleRowKeysCallableTest() {
@Test
public void proxySampleRowKeysTest() {
- Mockito.when(mockStub.sampleRowKeysCallable()).thenReturn(mockSampleRowKeysCallable);
+ Mockito.when(mockStub.sampleRowKeysCallableWithRequest())
+ .thenReturn(mockSampleRowKeysCallableWithRequest);
bigtableDataClient.sampleRowKeysAsync("fake-table");
- Mockito.verify(mockSampleRowKeysCallable).futureCall("fake-table");
+ Mockito.verify(mockSampleRowKeysCallableWithRequest)
+ .futureCall(SampleRowKeysRequest.create(TableId.of("fake-table")));
+ }
+
+ @Test
+ public void proxySampleRowKeysOnAuthorizedViewTest() {
+ Mockito.when(mockStub.sampleRowKeysCallableWithRequest())
+ .thenReturn(mockSampleRowKeysCallableWithRequest);
+
+ bigtableDataClient.sampleRowKeysAsync(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ Mockito.verify(mockSampleRowKeysCallableWithRequest)
+ .futureCall(
+ SampleRowKeysRequest.create(AuthorizedViewId.of("fake-table", "fake-authorized-view")));
}
@Test
public void sampleRowKeysTest() {
- Mockito.when(mockStub.sampleRowKeysCallable()).thenReturn(mockSampleRowKeysCallable);
+ Mockito.when(mockStub.sampleRowKeysCallableWithRequest())
+ .thenReturn(mockSampleRowKeysCallableWithRequest);
- Mockito.when(mockSampleRowKeysCallable.futureCall(ArgumentMatchers.any(String.class)))
+ Mockito.when(
+ mockSampleRowKeysCallableWithRequest.futureCall(
+ ArgumentMatchers.any(SampleRowKeysRequest.class)))
.thenReturn(ApiFutures.immediateFuture(Collections.emptyList()));
bigtableDataClient.sampleRowKeys("fake-table");
- Mockito.verify(mockSampleRowKeysCallable).futureCall("fake-table");
+ Mockito.verify(mockSampleRowKeysCallableWithRequest)
+ .futureCall(SampleRowKeysRequest.create(TableId.of("fake-table")));
+ }
+
+ @Test
+ public void sampleRowKeysOnAuthorizedViewTest() {
+ Mockito.when(mockStub.sampleRowKeysCallableWithRequest())
+ .thenReturn(mockSampleRowKeysCallableWithRequest);
+
+ Mockito.when(
+ mockSampleRowKeysCallableWithRequest.futureCall(
+ ArgumentMatchers.any(SampleRowKeysRequest.class)))
+ .thenReturn(ApiFutures.immediateFuture(Collections.emptyList()));
+ bigtableDataClient.sampleRowKeys(AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ Mockito.verify(mockSampleRowKeysCallableWithRequest)
+ .futureCall(
+ SampleRowKeysRequest.create(AuthorizedViewId.of("fake-table", "fake-authorized-view")));
}
@Test
@@ -416,6 +703,18 @@ public void proxyMutateRowTest() {
Mockito.verify(mockMutateRowCallable).futureCall(request);
}
+ @Test
+ public void proxyMutateRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.mutateRowCallable()).thenReturn(mockMutateRowCallable);
+
+ RowMutation request =
+ RowMutation.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"), "some-key")
+ .setCell("some-family", "fake-qualifier", "fake-value");
+
+ bigtableDataClient.mutateRowAsync(request);
+ Mockito.verify(mockMutateRowCallable).futureCall(request);
+ }
+
@Test
public void mutateRowTest() {
Mockito.when(mockStub.mutateRowCallable()).thenReturn(mockMutateRowCallable);
@@ -431,6 +730,21 @@ public void mutateRowTest() {
Mockito.verify(mockMutateRowCallable).futureCall(request);
}
+ @Test
+ public void mutateRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.mutateRowCallable()).thenReturn(mockMutateRowCallable);
+ Mockito.when(mockMutateRowCallable.futureCall(ArgumentMatchers.any(RowMutation.class)))
+ .thenAnswer(
+ (Answer) invocationOnMock -> ApiFutures.immediateFuture(Empty.getDefaultInstance()));
+
+ RowMutation request =
+ RowMutation.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"), "some-key")
+ .setCell("some-family", "fake-qualifier", "fake-value");
+
+ bigtableDataClient.mutateRow(request);
+ Mockito.verify(mockMutateRowCallable).futureCall(request);
+ }
+
@Test
public void proxyBulkMutatesRowTest() {
Mockito.when(mockStub.bulkMutateRowsCallable()).thenReturn(mockBulkMutateRowsCallable);
@@ -445,6 +759,20 @@ public void proxyBulkMutatesRowTest() {
Mockito.verify(mockBulkMutateRowsCallable).futureCall(request);
}
+ @Test
+ public void proxyBulkMutatesRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.bulkMutateRowsCallable()).thenReturn(mockBulkMutateRowsCallable);
+
+ BulkMutation request =
+ BulkMutation.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .add(
+ "fake-key",
+ Mutation.create().setCell("fake-family", "fake-qualifier", "fake-value"));
+
+ bigtableDataClient.bulkMutateRowsAsync(request);
+ Mockito.verify(mockBulkMutateRowsCallable).futureCall(request);
+ }
+
@Test
public void bulkMutatesRowTest() {
Mockito.when(mockStub.bulkMutateRowsCallable()).thenReturn(mockBulkMutateRowsCallable);
@@ -463,6 +791,24 @@ public void bulkMutatesRowTest() {
Mockito.verify(mockBulkMutateRowsCallable).futureCall(request);
}
+ @Test
+ public void bulkMutatesRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.bulkMutateRowsCallable()).thenReturn(mockBulkMutateRowsCallable);
+
+ Mockito.when(mockBulkMutateRowsCallable.futureCall(ArgumentMatchers.any(BulkMutation.class)))
+ .thenAnswer(
+ (Answer) invocationOnMock -> ApiFutures.immediateFuture(Empty.getDefaultInstance()));
+
+ BulkMutation request =
+ BulkMutation.create(AuthorizedViewId.of("fake-table", "fake-authorized-view"))
+ .add(
+ "fake-key",
+ Mutation.create().setCell("fake-family", "fake-qualifier", "fake-value"));
+
+ bigtableDataClient.bulkMutateRows(request);
+ Mockito.verify(mockBulkMutateRowsCallable).futureCall(request);
+ }
+
@Test
public void proxyNewBulkMutationBatcherTest() {
Mockito.when(mockStub.newMutateRowsBatcher(Mockito.any(String.class), Mockito.any()))
@@ -481,6 +827,25 @@ public void proxyNewBulkMutationBatcherTest() {
Mockito.verify(mockStub).newMutateRowsBatcher(Mockito.any(String.class), Mockito.any());
}
+ @Test
+ public void proxyNewBulkMutationBatcherOnAuthorizedViewTest() {
+ Mockito.when(mockStub.newMutateRowsBatcher(Mockito.any(TargetId.class), Mockito.any()))
+ .thenReturn(mockBulkMutationBatcher);
+
+ ApiFuture expectedResponse = ApiFutures.immediateFuture(null);
+ Batcher batcher =
+ bigtableDataClient.newBulkMutationBatcher(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ RowMutationEntry request =
+ RowMutationEntry.create("some-key").setCell("some-family", "fake-qualifier", "fake-value");
+ Mockito.when(mockBulkMutationBatcher.add(request)).thenReturn(expectedResponse);
+
+ ApiFuture actualRes = batcher.add(request);
+ assertThat(actualRes).isSameInstanceAs(expectedResponse);
+
+ Mockito.verify(mockStub).newMutateRowsBatcher(Mockito.any(TargetId.class), Mockito.any());
+ }
+
@Test
public void proxyNewBulkReadRowsTest() {
Mockito.when(mockStub.newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any()))
@@ -500,6 +865,27 @@ public void proxyNewBulkReadRowsTest() {
Mockito.verify(mockStub).newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any());
}
+ @Test
+ public void proxyNewBulkReadRowsOnAuthorizedViewTest() {
+ Mockito.when(mockStub.newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any()))
+ .thenReturn(mockBulkReadRowsBatcher);
+
+ ApiFuture expectedResponse =
+ ApiFutures.immediateFuture(
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), Collections.emptyList()));
+ ByteString request = ByteString.copyFromUtf8("fake-row-key");
+
+ Batcher batcher =
+ bigtableDataClient.newBulkReadRowsBatcher(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"));
+ Mockito.when(mockBulkReadRowsBatcher.add(request)).thenReturn(expectedResponse);
+
+ ApiFuture actualResponse = batcher.add(request);
+ assertThat(actualResponse).isSameInstanceAs(expectedResponse);
+
+ Mockito.verify(mockStub).newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any());
+ }
+
@Test
public void proxyNewBulkReadRowsWithFilterTest() {
Mockito.when(mockStub.newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any()))
@@ -520,6 +906,28 @@ public void proxyNewBulkReadRowsWithFilterTest() {
Mockito.verify(mockStub).newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any());
}
+ @Test
+ public void proxyNewBulkReadRowsOnAuthorizedViewWithFilterTest() {
+ Mockito.when(mockStub.newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any()))
+ .thenReturn(mockBulkReadRowsBatcher);
+
+ ApiFuture expectedResponse =
+ ApiFutures.immediateFuture(
+ Row.create(ByteString.copyFromUtf8("fake-row-key"), Collections.emptyList()));
+ ByteString request = ByteString.copyFromUtf8("fake-row-key");
+
+ Batcher batcher =
+ bigtableDataClient.newBulkReadRowsBatcher(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"),
+ FILTERS.key().regex("fake-row"));
+ Mockito.when(mockBulkReadRowsBatcher.add(request)).thenReturn(expectedResponse);
+
+ ApiFuture actualResponse = batcher.add(request);
+ assertThat(actualResponse).isSameInstanceAs(expectedResponse);
+
+ Mockito.verify(mockStub).newBulkReadRowsBatcher(Mockito.any(Query.class), Mockito.any());
+ }
+
@Test
public void proxyCheckAndMutateRowCallableTest() {
assertThat(bigtableDataClient.checkAndMutateRowCallable())
@@ -538,6 +946,19 @@ public void proxyCheckAndMutateRowTest() {
Mockito.verify(mockCheckAndMutateRowCallable).futureCall(mutation);
}
+ @Test
+ public void proxyCheckAndMutateRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.checkAndMutateRowCallable()).thenReturn(mockCheckAndMutateRowCallable);
+
+ ConditionalRowMutation mutation =
+ ConditionalRowMutation.create(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-key")
+ .then(Mutation.create().setCell("fake-family", "fake-qualifier", "fake-value"));
+ bigtableDataClient.checkAndMutateRowAsync(mutation);
+
+ Mockito.verify(mockCheckAndMutateRowCallable).futureCall(mutation);
+ }
+
@Test
public void checkAndMutateRowTest() {
Mockito.when(mockStub.checkAndMutateRowCallable()).thenReturn(mockCheckAndMutateRowCallable);
@@ -554,6 +975,23 @@ public void checkAndMutateRowTest() {
Mockito.verify(mockCheckAndMutateRowCallable).futureCall(mutation);
}
+ @Test
+ public void checkAndMutateRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.checkAndMutateRowCallable()).thenReturn(mockCheckAndMutateRowCallable);
+
+ Mockito.when(
+ mockCheckAndMutateRowCallable.futureCall(
+ ArgumentMatchers.any(ConditionalRowMutation.class)))
+ .thenReturn(ApiFutures.immediateFuture(Boolean.TRUE));
+ ConditionalRowMutation mutation =
+ ConditionalRowMutation.create(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "fake-key")
+ .then(Mutation.create().setCell("fake-family", "fake-qualifier", "fake-value"));
+ bigtableDataClient.checkAndMutateRow(mutation);
+
+ Mockito.verify(mockCheckAndMutateRowCallable).futureCall(mutation);
+ }
+
@Test
public void proxyReadModifyWriteRowTest() {
Mockito.when(mockStub.readModifyWriteRowCallable()).thenReturn(mockReadModifyWriteRowCallable);
@@ -565,6 +1003,18 @@ public void proxyReadModifyWriteRowTest() {
Mockito.verify(mockReadModifyWriteRowCallable).futureCall(request);
}
+ @Test
+ public void proxyReadModifyWriteRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.readModifyWriteRowCallable()).thenReturn(mockReadModifyWriteRowCallable);
+
+ ReadModifyWriteRow request =
+ ReadModifyWriteRow.create(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "some-key")
+ .append("fake-family", "fake-qualifier", "suffix");
+ bigtableDataClient.readModifyWriteRowAsync(request);
+ Mockito.verify(mockReadModifyWriteRowCallable).futureCall(request);
+ }
+
@Test
public void readModifyWriteRowTest() {
Mockito.when(mockStub.readModifyWriteRowCallable()).thenReturn(mockReadModifyWriteRowCallable);
@@ -583,6 +1033,25 @@ public void readModifyWriteRowTest() {
Mockito.verify(mockReadModifyWriteRowCallable).futureCall(request);
}
+ @Test
+ public void readModifyWriteRowOnAuthorizedViewTest() {
+ Mockito.when(mockStub.readModifyWriteRowCallable()).thenReturn(mockReadModifyWriteRowCallable);
+
+ Mockito.when(
+ mockReadModifyWriteRowCallable.futureCall(
+ ArgumentMatchers.any(ReadModifyWriteRow.class)))
+ .thenReturn(
+ ApiFutures.immediateFuture(
+ Row.create(
+ ByteString.copyFromUtf8("fake-row-key"), Collections.emptyList())));
+ ReadModifyWriteRow request =
+ ReadModifyWriteRow.create(
+ AuthorizedViewId.of("fake-table", "fake-authorized-view"), "some-key")
+ .append("fake-family", "fake-qualifier", "suffix");
+ bigtableDataClient.readModifyWriteRow(request);
+ Mockito.verify(mockReadModifyWriteRowCallable).futureCall(request);
+ }
+
@Test
public void proxyReadModifyWriterRowCallableTest() {
Mockito.when(mockStub.readModifyWriteRowCallable()).thenReturn(mockReadModifyWriteRowCallable);
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java
index a09d9415f5..a284f8b7cb 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkMutateIT.java
@@ -15,14 +15,21 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+import com.google.api.gax.batching.Batcher;
import com.google.api.gax.batching.BatcherImpl;
import com.google.api.gax.batching.BatchingSettings;
import com.google.api.gax.batching.FlowControlEventStats;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Row;
@@ -88,6 +95,50 @@ public void test() throws IOException, InterruptedException {
}
}
+ @Test(timeout = 60 * 1000)
+ public void testOnAuthorizedView() throws IOException, InterruptedException {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ BigtableDataSettings settings = testEnvRule.env().getDataClientSettings();
+ String rowPrefix = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+ // Set target latency really low so it'll trigger adjusting thresholds
+ BigtableDataSettings.Builder builder =
+ settings.toBuilder().enableBatchMutationLatencyBasedThrottling(2L);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ try (BigtableDataClient client = BigtableDataClient.create(builder.build());
+ Batcher batcher =
+ client.newBulkMutationBatcher(
+ AuthorizedViewId.of(testEnvRule.env().getTableId(), testAuthorizedView.getId()))) {
+
+ String familyId = testEnvRule.env().getFamilyId();
+
+ batcher.add(
+ RowMutationEntry.create(rowPrefix + "test-key")
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ batcher.flush();
+
+ // Query a key to make sure the write succeeded
+ Row row =
+ testEnvRule
+ .env()
+ .getDataClient()
+ .readRowsCallable()
+ .first()
+ .call(Query.create(testEnvRule.env().getTableId()).rowKey(rowPrefix + "test-key"));
+ assertThat(row.getCells()).hasSize(1);
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
+
@Test
public void testManyMutations() throws IOException, InterruptedException {
// Emulator is very slow and will take a long time for the test to run
@@ -135,4 +186,74 @@ public void testManyMutations() throws IOException, InterruptedException {
assertThat(row.getCells()).hasSize(100002);
}
}
+
+ @Test(timeout = 60 * 1000)
+ public void testManyMutationsOnAuthorizedView() throws IOException, InterruptedException {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ BigtableDataSettings settings = testEnvRule.env().getDataClientSettings();
+ String rowPrefix = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+
+ BatchingSettings batchingSettings =
+ settings.getStubSettings().bulkMutateRowsSettings().getBatchingSettings();
+
+ settings
+ .toBuilder()
+ .stubSettings()
+ .bulkMutateRowsSettings()
+ .setBatchingSettings(
+ batchingSettings.toBuilder().setDelayThreshold(Duration.ofHours(1)).build());
+ try (BigtableDataClient client = BigtableDataClient.create(settings);
+ Batcher batcher =
+ client.newBulkMutationBatcher(
+ AuthorizedViewId.of(testEnvRule.env().getTableId(), testAuthorizedView.getId()))) {
+ String familyId = testEnvRule.env().getFamilyId();
+ for (int i = 0; i < 2; i++) {
+ String key = rowPrefix + "test-key";
+ RowMutationEntry rowMutationEntry = RowMutationEntry.create(key);
+ // Create mutation entries with many columns. The batcher should flush every time.
+ for (long j = 0; j < 50001; j++) {
+ rowMutationEntry.setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + j + i, j);
+ }
+ batcher.add(rowMutationEntry);
+ }
+ batcher.flush();
+ // Query a key to make sure the write succeeded
+ Row row =
+ client
+ .readRowsCallable()
+ .first()
+ .call(Query.create(testEnvRule.env().getTableId()).rowKey(rowPrefix + "test-key"));
+ assertThat(row.getCells()).hasSize(100002);
+ }
+
+ // We should not be able to mutate rows outside the authorized view
+ try {
+ try (BigtableDataClient client = BigtableDataClient.create(settings);
+ Batcher batcherOutsideAuthorizedView =
+ client.newBulkMutationBatcher(
+ AuthorizedViewId.of(
+ testEnvRule.env().getTableId(), testAuthorizedView.getId()))) {
+ String keyOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+ RowMutationEntry rowMutationEntry = RowMutationEntry.create(keyOutsideAuthorizedView);
+ rowMutationEntry.setCell(
+ testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "test-value");
+ batcherOutsideAuthorizedView.add(rowMutationEntry);
+ batcherOutsideAuthorizedView.flush();
+ }
+ fail("Should not be able to apply bulk mutation on rows outside authorized view");
+ } catch (Exception e) {
+ // Ignore.
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkReadIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkReadIT.java
index 99c14ccc4f..5b72328240 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkReadIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/BulkReadIT.java
@@ -15,16 +15,21 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.api.gax.batching.Batcher;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.Row;
import com.google.cloud.bigtable.data.v2.models.RowCell;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
@@ -41,6 +46,8 @@
public class BulkReadIT {
@ClassRule public static TestEnvRule testEnvRule = new TestEnvRule();
+ private static String AUTHORIZED_VIEW_ROW_PREFIX = "row#";
+ private static String AUTHORIZED_VIEW_COLUMN_QUALIFIER = "qualifier";
@Test
public void testBulkRead() throws InterruptedException, ExecutionException {
@@ -102,4 +109,87 @@ public void testBulkRead() throws InterruptedException, ExecutionException {
assertThat(actualRows.get(2)).isEqualTo(expectedRows.get(0));
}
}
+
+ @Test
+ public void testBulkReadOnAuthorizedView() throws InterruptedException, ExecutionException {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ BigtableDataClient client = testEnvRule.env().getDataClient();
+ String family = testEnvRule.env().getFamilyId();
+ String rowPrefix = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+ int numRows = 10;
+
+ BulkMutation bulkMutation = BulkMutation.create(testEnvRule.env().getTableId());
+ List expectedRows = new ArrayList<>();
+
+ for (int i = 0; i < numRows; i++) {
+ bulkMutation.add(
+ RowMutationEntry.create(rowPrefix + "-" + i)
+ .setCell(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER, 10_000L, "value-" + i));
+ expectedRows.add(
+ Row.create(
+ ByteString.copyFromUtf8(rowPrefix + "-" + i),
+ ImmutableList.of(
+ RowCell.create(
+ family,
+ ByteString.copyFromUtf8(AUTHORIZED_VIEW_COLUMN_QUALIFIER),
+ 10_000L,
+ ImmutableList.of(),
+ ByteString.copyFromUtf8("value-" + i)))));
+ }
+ // Add a row outside the authorized view.
+ String rowPrefixForRowOutsideAuthorizedView = rowPrefix + numRows;
+ bulkMutation.add(
+ RowMutationEntry.create(rowPrefixForRowOutsideAuthorizedView)
+ .setCell(family, "outside-authorized-view", 10_000L, "test-value"));
+ client.bulkMutateRows(bulkMutation);
+
+ try (Batcher batcher =
+ client.newBulkReadRowsBatcher(
+ AuthorizedViewId.of(testEnvRule.env().getTableId(), testAuthorizedView.getId()))) {
+
+ List> rowFutures = new ArrayList<>(numRows);
+
+ for (int rowCount = 0; rowCount < numRows; rowCount++) {
+ ApiFuture entryResponse =
+ batcher.add(ByteString.copyFromUtf8(rowPrefix + "-" + rowCount));
+
+ rowFutures.add(entryResponse);
+ }
+
+ batcher.flush();
+ List actualRows = ApiFutures.allAsList(rowFutures).get();
+ assertThat(actualRows).isEqualTo(expectedRows);
+
+ // To verify non-existent and duplicate row keys
+ rowFutures = new ArrayList<>();
+
+ // non-existent row key
+ rowFutures.add(batcher.add(ByteString.copyFromUtf8(UUID.randomUUID().toString())));
+
+ // duplicate row key
+ rowFutures.add(batcher.add(ByteString.copyFromUtf8(rowPrefix + "-" + 0)));
+ rowFutures.add(batcher.add(ByteString.copyFromUtf8(rowPrefix + "-" + 0)));
+
+ // row key outside authorized view
+ rowFutures.add(batcher.add(ByteString.copyFromUtf8(rowPrefixForRowOutsideAuthorizedView)));
+
+ batcher.flush();
+ actualRows = ApiFutures.allAsList(rowFutures).get();
+ assertThat(actualRows.get(0)).isNull();
+ assertThat(actualRows.get(1)).isEqualTo(expectedRows.get(0));
+ assertThat(actualRows.get(2)).isEqualTo(expectedRows.get(0));
+ assertThat(actualRows.get(3)).isNull();
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java
index 5f53284690..41def01ba6 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/CheckAndMutateIT.java
@@ -16,13 +16,23 @@
package com.google.cloud.bigtable.data.v2.it;
import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+import com.google.api.gax.rpc.PermissionDeniedException;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
+import com.google.cloud.bigtable.data.v2.BigtableDataClient;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.ConditionalRowMutation;
import com.google.cloud.bigtable.data.v2.models.Mutation;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Row;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.protobuf.ByteString;
import java.util.UUID;
@@ -71,4 +81,81 @@ public void test() throws Exception {
assertThat(row.getCells()).hasSize(3);
assertThat(row.getCells().get(2).getValue()).isEqualTo(ByteString.copyFromUtf8("q1"));
}
+
+ @Test
+ public void testOnAuthorizedView() throws Exception {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ String tableId = testEnvRule.env().getTableId();
+ String familyId = testEnvRule.env().getFamilyId();
+ String rowKey = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+ BigtableDataClient dataClient = testEnvRule.env().getDataClient();
+
+ dataClient
+ .mutateRowCallable()
+ .call(
+ RowMutation.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "1", "val1")
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "2", "val2"));
+
+ dataClient
+ .checkAndMutateRowAsync(
+ ConditionalRowMutation.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .condition(FILTERS.qualifier().exactMatch(AUTHORIZED_VIEW_COLUMN_QUALIFIER + "1"))
+ .then(
+ Mutation.create()
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "3", "q1")))
+ .get(1, TimeUnit.MINUTES);
+
+ Row row = dataClient.readRowsCallable().first().call(Query.create(tableId).rowKey(rowKey));
+
+ assertThat(row.getCells()).hasSize(3);
+ assertThat(row.getCells().get(2).getValue()).isEqualTo(ByteString.copyFromUtf8("q1"));
+
+ // Conditional mutation for rows exist in the table but outside the authorized view
+ String rowKeyOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+ dataClient
+ .mutateRowCallable()
+ .call(
+ RowMutation.create(tableId, rowKeyOutsideAuthorizedView)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ try {
+ dataClient
+ .checkAndMutateRowAsync(
+ ConditionalRowMutation.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ rowKeyOutsideAuthorizedView)
+ .condition(FILTERS.qualifier().exactMatch(AUTHORIZED_VIEW_COLUMN_QUALIFIER))
+ .then(Mutation.create().setCell(familyId, "new_qualifier", "new-value")))
+ .get(1, TimeUnit.MINUTES);
+ fail("Should not be able to conditional mutate row outside authorized view");
+ } catch (Exception e) {
+ assertThat(e.getCause()).isInstanceOf(PermissionDeniedException.class);
+ }
+
+ // Column qualifier outside the authorized view
+ try {
+ dataClient
+ .checkAndMutateRowAsync(
+ ConditionalRowMutation.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .condition(FILTERS.qualifier().exactMatch(AUTHORIZED_VIEW_COLUMN_QUALIFIER))
+ .then(Mutation.create().setCell(familyId, "new_qualifier", "new-value")))
+ .get(1, TimeUnit.MINUTES);
+ fail("Should not be able to perform mutations with cells outside the authorized view");
+ } catch (Exception e) {
+ assertThat(e.getCause()).isInstanceOf(PermissionDeniedException.class);
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java
index 2774cbc648..c99000be48 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/MutateRowIT.java
@@ -15,11 +15,20 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+import com.google.api.gax.rpc.PermissionDeniedException;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Row;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.protobuf.ByteString;
import java.util.UUID;
@@ -70,4 +79,77 @@ public void test() throws Exception {
assertThat(row.getCells().get(2).getValue())
.isEqualTo(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78}));
}
+
+ @Test
+ public void testOnAuthorizedView() throws Exception {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ String rowKey = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+ String familyId = testEnvRule.env().getFamilyId();
+
+ testEnvRule
+ .env()
+ .getDataClient()
+ .mutateRowAsync(
+ RowMutation.create(
+ AuthorizedViewId.of(testEnvRule.env().getTableId(), testAuthorizedView.getId()),
+ rowKey)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, "myVal")
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "2", "myVal2")
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "3", "myVal3")
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "4", 0x12345678))
+ .get(1, TimeUnit.MINUTES);
+
+ testEnvRule
+ .env()
+ .getDataClient()
+ .mutateRowAsync(
+ RowMutation.create(
+ AuthorizedViewId.of(testEnvRule.env().getTableId(), testAuthorizedView.getId()),
+ rowKey)
+ .deleteCells(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "2"))
+ .get(1, TimeUnit.MINUTES);
+
+ Row row =
+ testEnvRule
+ .env()
+ .getDataClient()
+ .readRowsCallable()
+ .first()
+ .call(Query.create(testEnvRule.env().getTableId()).rowKey(rowKey));
+
+ assertThat(row.getCells()).hasSize(3);
+ assertThat(row.getCells().get(0).getValue()).isEqualTo(ByteString.copyFromUtf8("myVal"));
+ assertThat(row.getCells().get(1).getValue()).isEqualTo(ByteString.copyFromUtf8("myVal3"));
+ assertThat(row.getCells().get(2).getValue())
+ .isEqualTo(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78}));
+
+ // We should not be able to mutate a row outside the authorized view
+ try {
+ String rowKeyOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+ testEnvRule
+ .env()
+ .getDataClient()
+ .mutateRowAsync(
+ RowMutation.create(
+ AuthorizedViewId.of(
+ testEnvRule.env().getTableId(), testAuthorizedView.getId()),
+ rowKeyOutsideAuthorizedView)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, "myVal"))
+ .get(1, TimeUnit.MINUTES);
+ fail("Should not be able to mutate row outside authorized view");
+ } catch (Exception e) {
+ assertThat(e.getCause()).isInstanceOf(PermissionDeniedException.class);
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java
index 6578dbad24..95ed16817e 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java
@@ -15,6 +15,9 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
@@ -27,8 +30,10 @@
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.api.gax.rpc.ResponseObserver;
import com.google.api.gax.rpc.StreamController;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Range.ByteStringRange;
@@ -97,6 +102,55 @@ public void isRowExists() throws Exception {
assertThat(testEnvRule.env().getDataClient().existsAsync(tableId, rowKey).get()).isTrue();
}
+ @Test
+ public void isRowExistsOnAuthorizedView() throws Exception {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ BigtableDataClient dataClient = testEnvRule.env().getDataClient();
+ String tableId = testEnvRule.env().getTableId();
+ String rowKey = AUTHORIZED_VIEW_ROW_PREFIX + prefix + "-isRowExistsOnAuthorizedView";
+ String rowKeyOutsideAuthorizedView = prefix + "-isRowExistsOnAuthorizedView";
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ dataClient.mutateRow(
+ RowMutation.create(tableId, rowKey)
+ .setCell(testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ dataClient.mutateRow(
+ RowMutation.create(tableId, rowKeyOutsideAuthorizedView)
+ .setCell(testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+
+ assertThat(dataClient.exists(AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey))
+ .isTrue();
+ assertThat(
+ dataClient.exists(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ rowKeyOutsideAuthorizedView))
+ .isFalse();
+
+ // Async
+ assertThat(
+ dataClient
+ .existsAsync(AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .get())
+ .isTrue();
+ assertThat(
+ dataClient
+ .existsAsync(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ rowKeyOutsideAuthorizedView)
+ .get())
+ .isFalse();
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(tableId, testAuthorizedView.getId());
+ }
+
@Test
public void readEmpty() throws Throwable {
String uniqueKey = prefix + "-readEmpty";
@@ -114,6 +168,55 @@ public void readEmpty() throws Throwable {
assertThat(observer.responses).isEmpty();
}
+ @Test
+ public void readEmptyOnAuthorizedView() throws Throwable {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ String tableId = testEnvRule.env().getTableId();
+ BigtableDataClient dataClient = testEnvRule.env().getDataClient();
+ String uniqueKey = AUTHORIZED_VIEW_ROW_PREFIX + prefix + "-readEmptyOnAuthorizedView";
+ String uniqueKeyOutsideAuthorizedView = prefix + "-readEmptyOnAuthorizedView";
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ Query query =
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId())).rowKey(uniqueKey);
+ Query queryOutsideAuthorizedView =
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .rowKey(uniqueKeyOutsideAuthorizedView);
+
+ // Sync
+ ArrayList rows = Lists.newArrayList(dataClient.readRows(query));
+ assertThat(rows).isEmpty();
+
+ // Row exists on the table but outside the authorized view
+ dataClient.mutateRow(
+ RowMutation.create(tableId, uniqueKeyOutsideAuthorizedView)
+ .setCell(testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ rows = Lists.newArrayList(dataClient.readRows(queryOutsideAuthorizedView));
+ assertThat(rows).isEmpty();
+
+ // Async
+ AccumulatingObserver observer = new AccumulatingObserver();
+ testEnvRule.env().getDataClient().readRowsAsync(query, observer);
+ observer.awaitCompletion();
+ assertThat(observer.responses).isEmpty();
+
+ // Row exists on the table but outside the authorized view
+ observer = new AccumulatingObserver();
+ testEnvRule.env().getDataClient().readRowsAsync(queryOutsideAuthorizedView, observer);
+ observer.awaitCompletion();
+ assertThat(observer.responses).isEmpty();
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(tableId, testAuthorizedView.getId());
+ }
+
@Test
public void read() throws Throwable {
int numRows = 5;
@@ -169,6 +272,122 @@ public void read() throws Throwable {
assertThat(actualRowFuture.get()).isEqualTo(expectedRows.get(0));
}
+ @Test
+ public void readOnAuthorizedView() throws Throwable {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ int numRows = 5;
+ List expectedRows = Lists.newArrayList();
+ String uniqueKey = AUTHORIZED_VIEW_ROW_PREFIX + prefix + "-readOnAuthorizedView";
+ String uniqueKeyOutsideAuthorizedView = prefix + "-readOnAuthorizedView";
+ String tableId = testEnvRule.env().getTableId();
+ BigtableDataClient dataClient = testEnvRule.env().getDataClient();
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ long timestampMicros = System.currentTimeMillis() * 1_000;
+
+ for (int i = 0; i < numRows; i++) {
+ dataClient
+ .mutateRowCallable()
+ .call(
+ RowMutation.create(tableId, uniqueKey + "-" + i)
+ .setCell(
+ testEnvRule.env().getFamilyId(),
+ AUTHORIZED_VIEW_COLUMN_QUALIFIER,
+ timestampMicros,
+ "my-value"));
+
+ expectedRows.add(
+ Row.create(
+ ByteString.copyFromUtf8(uniqueKey + "-" + i),
+ ImmutableList.of(
+ RowCell.create(
+ testEnvRule.env().getFamilyId(),
+ ByteString.copyFromUtf8(AUTHORIZED_VIEW_COLUMN_QUALIFIER),
+ timestampMicros,
+ ImmutableList.of(),
+ ByteString.copyFromUtf8("my-value")))));
+ }
+ // Add a few rows that outside the authorized view
+ for (int i = 0; i < numRows; i++) {
+ dataClient
+ .mutateRowCallable()
+ .call(
+ RowMutation.create(tableId, uniqueKeyOutsideAuthorizedView + "-" + i)
+ .setCell(
+ testEnvRule.env().getFamilyId(),
+ AUTHORIZED_VIEW_COLUMN_QUALIFIER,
+ timestampMicros,
+ "my-value"));
+ }
+
+ // Sync
+ Query query =
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(uniqueKey + "-0", uniqueKey + "-" + numRows);
+ Query queryOutsideAuthorizedView =
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(
+ uniqueKeyOutsideAuthorizedView + "-0",
+ uniqueKeyOutsideAuthorizedView + "-" + numRows);
+
+ ArrayList actualResults = Lists.newArrayList(dataClient.readRows(query));
+ assertThat(actualResults).containsExactlyElementsIn(expectedRows);
+
+ // rows exist but outside the authorized view
+ ArrayList results = Lists.newArrayList(dataClient.readRows(queryOutsideAuthorizedView));
+ assertThat(results).isEmpty();
+
+ // Async
+ AccumulatingObserver observer = new AccumulatingObserver();
+ dataClient.readRowsAsync(query, observer);
+ observer.awaitCompletion();
+ assertThat(observer.responses).containsExactlyElementsIn(expectedRows);
+
+ // Rows exist but outside the authorized view
+ observer = new AccumulatingObserver();
+ dataClient.readRowsAsync(queryOutsideAuthorizedView, observer);
+ observer.awaitCompletion();
+ assertThat(observer.responses).isEmpty();
+
+ // Point Sync
+ Row actualRow =
+ dataClient.readRow(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), expectedRows.get(0).getKey());
+ assertThat(actualRow).isEqualTo(expectedRows.get(0));
+
+ // Row exists but outside the authorized view
+ assertThat(
+ dataClient.readRow(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ uniqueKeyOutsideAuthorizedView + "-0"))
+ .isNull();
+
+ // Point Async
+ ApiFuture actualRowFuture =
+ dataClient.readRowAsync(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), expectedRows.get(0).getKey());
+ assertThat(actualRowFuture.get()).isEqualTo(expectedRows.get(0));
+
+ // Row exists but outside the authorized view
+ assertThat(
+ dataClient
+ .readRowAsync(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ uniqueKeyOutsideAuthorizedView + "-0")
+ .get())
+ .isNull();
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(tableId, testAuthorizedView.getId());
+ }
+
@Test
public void rangeQueries() {
BigtableDataClient client = testEnvRule.env().getDataClient();
@@ -240,6 +459,101 @@ public void rangeQueries() {
.isEmpty();
}
+ @Test
+ public void rangeQueriesOnAuthorizedView() {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ BigtableDataClient client = testEnvRule.env().getDataClient();
+ String tableId = testEnvRule.env().getTableId();
+ String familyId = testEnvRule.env().getFamilyId();
+ String uniqueKey = AUTHORIZED_VIEW_ROW_PREFIX + prefix + "-rangeQueriesOnAuthorizedView";
+ String keyA = uniqueKey + "-" + "a";
+ String keyZ = uniqueKey + "-" + "z";
+ String keyOutsideAuthorizedView = prefix;
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ long timestampMicros = System.currentTimeMillis() * 1_000;
+
+ client.bulkMutateRows(
+ BulkMutation.create(tableId)
+ .add(
+ RowMutationEntry.create(keyA)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, timestampMicros, "A"))
+ .add(
+ RowMutationEntry.create(keyZ)
+ .setCell(familyId, AUTHORIZED_VIEW_COLUMN_QUALIFIER, timestampMicros, "Z"))
+ .add(
+ RowMutationEntry.create(keyOutsideAuthorizedView)
+ .setCell(
+ familyId,
+ AUTHORIZED_VIEW_COLUMN_QUALIFIER,
+ timestampMicros,
+ "outsideAuthorizedView")));
+
+ Row expectedRowA =
+ Row.create(
+ ByteString.copyFromUtf8(keyA),
+ ImmutableList.of(
+ RowCell.create(
+ testEnvRule.env().getFamilyId(),
+ ByteString.copyFromUtf8(AUTHORIZED_VIEW_COLUMN_QUALIFIER),
+ timestampMicros,
+ ImmutableList.of(),
+ ByteString.copyFromUtf8("A"))));
+
+ Row expectedRowZ =
+ Row.create(
+ ByteString.copyFromUtf8(keyZ),
+ ImmutableList.of(
+ RowCell.create(
+ testEnvRule.env().getFamilyId(),
+ ByteString.copyFromUtf8(AUTHORIZED_VIEW_COLUMN_QUALIFIER),
+ timestampMicros,
+ ImmutableList.of(),
+ ByteString.copyFromUtf8("Z"))));
+
+ // Closed/Open
+ assertThat(
+ ImmutableList.copyOf(
+ client.readRows(
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(ByteStringRange.unbounded().startClosed(keyA).endOpen(keyZ)))))
+ .containsExactly(expectedRowA);
+
+ // Closed/Closed
+ assertThat(
+ ImmutableList.copyOf(
+ client.readRows(
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(ByteStringRange.unbounded().startClosed(keyA).endClosed(keyZ)))))
+ .containsExactly(expectedRowA, expectedRowZ);
+
+ // Open/Closed
+ assertThat(
+ ImmutableList.copyOf(
+ client.readRows(
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(ByteStringRange.unbounded().startOpen(keyA).endClosed(keyZ)))))
+ .containsExactly(expectedRowZ);
+
+ // Open/Open
+ assertThat(
+ ImmutableList.copyOf(
+ client.readRows(
+ Query.create(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))
+ .range(ByteStringRange.unbounded().startOpen(keyA).endOpen(keyZ)))))
+ .isEmpty();
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(tableId, testAuthorizedView.getId());
+ }
+
@Test
public void reversed() {
assume()
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadModifyWriteIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadModifyWriteIT.java
index e00556211f..ef5cf83c75 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadModifyWriteIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadModifyWriteIT.java
@@ -15,10 +15,19 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+import com.google.api.gax.rpc.PermissionDeniedException;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
import com.google.cloud.bigtable.data.v2.models.Row;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.protobuf.ByteString;
import java.util.UUID;
@@ -58,4 +67,75 @@ public void test() throws InterruptedException, ExecutionException, TimeoutExcep
assertThat(row.getCells().get(2).getValue())
.isEqualTo(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0x12, 0x34, 0x56, 0x79}));
}
+
+ @Test
+ public void testOnAuthorizedView()
+ throws InterruptedException, ExecutionException, TimeoutException {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ String tableId = testEnvRule.env().getTableId();
+ String family = testEnvRule.env().getFamilyId();
+ String rowKey = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+
+ Row row =
+ testEnvRule
+ .env()
+ .getDataClient()
+ .readModifyWriteRowAsync(
+ ReadModifyWriteRow.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .append(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "1", "a")
+ .increment(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "2", 3)
+ .increment(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER + "3", 0x12345679))
+ .get(1, TimeUnit.MINUTES);
+
+ assertThat(row.getCells()).hasSize(3);
+ assertThat(row.getCells().get(0).getValue()).isEqualTo(ByteString.copyFromUtf8("a"));
+ assertThat(row.getCells().get(1).getValue())
+ .isEqualTo(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 3}));
+ assertThat(row.getCells().get(2).getValue())
+ .isEqualTo(ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0x12, 0x34, 0x56, 0x79}));
+
+ // Row key outside the authorized view
+ String rowKeyOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+ try {
+ testEnvRule
+ .env()
+ .getDataClient()
+ .readModifyWriteRowAsync(
+ ReadModifyWriteRow.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()),
+ rowKeyOutsideAuthorizedView)
+ .append(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER, "a"))
+ .get(1, TimeUnit.MINUTES);
+ fail("Should not be able to modify a row outside authorized view");
+ } catch (Exception e) {
+ assertThat(e.getCause()).isInstanceOf(PermissionDeniedException.class);
+ }
+
+ // Column qualifier outside the authorized view
+ try {
+ testEnvRule
+ .env()
+ .getDataClient()
+ .readModifyWriteRowAsync(
+ ReadModifyWriteRow.create(
+ AuthorizedViewId.of(tableId, testAuthorizedView.getId()), rowKey)
+ .append(family, "outside-authorized-view", "a"))
+ .get(1, TimeUnit.MINUTES);
+ fail("Should not be able to perform mutations with cells outside the authorized view");
+ } catch (Exception e) {
+ assertThat(e.getCause()).isInstanceOf(PermissionDeniedException.class);
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/RowMutationEntryBatcherIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/RowMutationEntryBatcherIT.java
index 4191a01ea6..6b2eaf2047 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/RowMutationEntryBatcherIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/RowMutationEntryBatcherIT.java
@@ -15,15 +15,23 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
import com.google.api.gax.batching.Batcher;
import com.google.api.gax.rpc.ServerStream;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Row;
import com.google.cloud.bigtable.data.v2.models.RowCell;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
@@ -72,4 +80,64 @@ public void testNewBatcher() throws Exception {
assertThat(actualRows).containsExactlyElementsIn(expectedRows);
}
+
+ @Test
+ public void testNewBatcherOnAuthorizedView() throws Exception {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ BigtableDataClient client = testEnvRule.env().getDataClient();
+ String tableId = testEnvRule.env().getTableId();
+ String family = testEnvRule.env().getFamilyId();
+ String rowPrefix = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+
+ try (Batcher batcher =
+ client.newBulkMutationBatcher(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))) {
+ for (int i = 0; i < 10; i++) {
+ batcher.add(
+ RowMutationEntry.create(rowPrefix + "-" + i)
+ .setCell(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER, 10_000L, "value-" + i));
+ }
+ }
+
+ List expectedRows = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ expectedRows.add(
+ Row.create(
+ ByteString.copyFromUtf8(rowPrefix + "-" + i),
+ ImmutableList.of(
+ RowCell.create(
+ family,
+ ByteString.copyFromUtf8(AUTHORIZED_VIEW_COLUMN_QUALIFIER),
+ 10_000L,
+ ImmutableList.of(),
+ ByteString.copyFromUtf8("value-" + i)))));
+ }
+ ServerStream actualRows = client.readRows(Query.create(tableId).prefix(rowPrefix));
+
+ assertThat(actualRows).containsExactlyElementsIn(expectedRows);
+
+ // We should not be able to mutate rows outside the authorized view
+ String rowKeyOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+ try {
+ try (Batcher batcher =
+ client.newBulkMutationBatcher(AuthorizedViewId.of(tableId, testAuthorizedView.getId()))) {
+ batcher.add(
+ RowMutationEntry.create(rowKeyOutsideAuthorizedView)
+ .setCell(family, AUTHORIZED_VIEW_COLUMN_QUALIFIER, 10_000L, "value"));
+ }
+ fail("Should not be able to apply bulk mutation on rows outside authorized view");
+ } catch (Exception e) {
+ // Ignore.
+ }
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java
index cb06243509..81fd553c8e 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/SampleRowsIT.java
@@ -15,13 +15,19 @@
*/
package com.google.cloud.bigtable.data.v2.it;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_COLUMN_QUALIFIER;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.AUTHORIZED_VIEW_ROW_PREFIX;
+import static com.google.cloud.bigtable.misc_utilities.AuthorizedViewTestHelper.createTestAuthorizedView;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
import com.google.cloud.bigtable.data.v2.models.KeyOffset;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
+import com.google.cloud.bigtable.test_helpers.env.EmulatorEnv;
import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
import com.google.common.collect.Lists;
import java.util.List;
@@ -61,4 +67,50 @@ public void test() throws InterruptedException, ExecutionException, TimeoutExcep
assertThat(results).isNotEmpty();
assertThat(results.get(results.size() - 1).getOffsetBytes()).isGreaterThan(0L);
}
+
+ @Test
+ public void testOnAuthorizedView()
+ throws InterruptedException, ExecutionException, TimeoutException {
+ assume()
+ .withMessage("AuthorizedView is not supported on Emulator")
+ .that(testEnvRule.env())
+ .isNotInstanceOf(EmulatorEnv.class);
+
+ AuthorizedView testAuthorizedView = createTestAuthorizedView(testEnvRule);
+
+ BigtableDataClient client = testEnvRule.env().getDataClient();
+ String rowPrefix = AUTHORIZED_VIEW_ROW_PREFIX + UUID.randomUUID();
+ String rowPrefixOutsideAuthorizedView = UUID.randomUUID() + "-outside-authorized-view";
+
+ // Create some data so that sample row keys has something to show
+ List> futures = Lists.newArrayList();
+ for (int i = 0; i < 10; i++) {
+ ApiFuture future =
+ client.mutateRowAsync(
+ RowMutation.create(testEnvRule.env().getTableId(), rowPrefix + "-" + i)
+ .setCell(
+ testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ futures.add(future);
+ ApiFuture futureOutsideAuthorizedView =
+ client.mutateRowAsync(
+ RowMutation.create(
+ testEnvRule.env().getTableId(), rowPrefixOutsideAuthorizedView + "-" + i)
+ .setCell(
+ testEnvRule.env().getFamilyId(), AUTHORIZED_VIEW_COLUMN_QUALIFIER, "value"));
+ futures.add(futureOutsideAuthorizedView);
+ }
+ ApiFutures.allAsList(futures).get(1, TimeUnit.MINUTES);
+
+ ApiFuture> future = client.sampleRowKeysAsync(testEnvRule.env().getTableId());
+
+ List results = future.get(1, TimeUnit.MINUTES);
+
+ assertThat(results).isNotEmpty();
+ assertThat(results.get(results.size() - 1).getOffsetBytes()).isGreaterThan(0L);
+
+ testEnvRule
+ .env()
+ .getTableAdminClient()
+ .deleteAuthorizedView(testEnvRule.env().getTableId(), testAuthorizedView.getId());
+ }
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewIdTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewIdTest.java
new file mode 100644
index 0000000000..b20a99ec11
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewIdTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.data.v2.models;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AuthorizedViewIdTest {
+ private static final String PROJECT_ID = "my-project";
+ private static final String INSTANCE_ID = "my-instance";
+ private static final String TABLE_ID = "my-table";
+ private static final String AUTHORIZED_VIEW_ID = "my-authorized-view";
+
+ @Test
+ public void testToResourceName() {
+ AuthorizedViewId authorizedViewId = AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID);
+
+ assertThat(authorizedViewId.toResourceName(PROJECT_ID, INSTANCE_ID))
+ .isEqualTo(
+ "projects/my-project/instances/my-instance/tables/my-table/authorizedViews/my-authorized-view");
+ }
+
+ @Test
+ public void testEquality() {
+ AuthorizedViewId authorizedViewId = AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID);
+
+ assertThat(authorizedViewId).isEqualTo(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+ assertThat(authorizedViewId)
+ .isNotEqualTo(AuthorizedViewId.of(TABLE_ID, "another-authorized-view"));
+ assertThat(authorizedViewId).isNotEqualTo(TableId.of(TABLE_ID));
+ }
+
+ @Test
+ public void testHashCode() {
+ AuthorizedViewId authorizedViewId = AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID);
+
+ assertThat(authorizedViewId.hashCode())
+ .isEqualTo(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID).hashCode());
+ assertThat(authorizedViewId.hashCode())
+ .isNotEqualTo(AuthorizedViewId.of(TABLE_ID, "another-authorized-view").hashCode());
+ assertThat(authorizedViewId.hashCode()).isNotEqualTo(TableId.of(TABLE_ID).hashCode());
+ }
+
+ @Test
+ public void testToString() {
+ AuthorizedViewId authorizedViewId = AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID);
+
+ assertThat(authorizedViewId.toString())
+ .isEqualTo("AuthorizedViewId{tableId=my-table, authorizedViewId=my-authorized-view}");
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java
index 0e4c992648..84108d4a78 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/BulkMutationTest.java
@@ -38,12 +38,14 @@ public class BulkMutationTest {
private static final String PROJECT_ID = "fake-project";
private static final String INSTANCE_ID = "fake-instance";
private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String APP_PROFILE = "fake-profile";
private static final RequestContext REQUEST_CONTEXT =
RequestContext.create(PROJECT_ID, INSTANCE_ID, APP_PROFILE);
@Test
public void test() throws ParseException {
+ // Test BulkMutation on a table.
BulkMutation m =
BulkMutation.create(TABLE_ID)
.add(
@@ -95,10 +97,34 @@ public void test() throws ParseException {
expected);
assertThat(actual).isEqualTo(expected.build());
+
+ // Test BulkMutation on an authorized view.
+ m =
+ BulkMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .add(
+ "key-a",
+ Mutation.create()
+ .setCell("fake-family1", "fake-qualifier1", 1_000, "fake-value1")
+ .setCell("fake-family2", "fake-qualifier2", 2_000, "fake-value2"))
+ .add(
+ ByteString.copyFromUtf8("key-b"),
+ Mutation.create().setCell("fake-family3", "fake-qualifier3", 3_000, "fake-value3"));
+
+ actual = m.toProto(REQUEST_CONTEXT);
+
+ expected
+ .clearTableName()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE);
+
+ assertThat(actual).isEqualTo(expected.build());
}
@Test
public void serializationTest() throws IOException, ClassNotFoundException {
+ // Test BulkMutation on a table.
BulkMutation expected =
BulkMutation.create(TABLE_ID)
.add(
@@ -114,29 +140,82 @@ public void serializationTest() throws IOException, ClassNotFoundException {
BulkMutation actual = (BulkMutation) ois.readObject();
assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
+
+ // Test BulkMutation on an authorized view.
+ expected =
+ BulkMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .add(
+ "key-a",
+ Mutation.create().setCell("fake-family1", "fake-qualifier1", 1_000, "fake-value1"));
+
+ bos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(expected);
+ oos.close();
+
+ ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+
+ actual = (BulkMutation) ois.readObject();
+ assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
}
@Test
public void cloneTest() {
- BulkMutation originalBulkMutation =
+ // Test BulkMutation on a table.
+ BulkMutation originalTableBulkMutation =
BulkMutation.create(TABLE_ID)
.add(
"test-rowKey",
Mutation.create().setCell("fake-family1", "fake-qualifier1", 12345, "fake-value1"));
- MutateRowsRequest originalRequest = originalBulkMutation.toProto(REQUEST_CONTEXT);
- BulkMutation clonedMutation = originalBulkMutation.clone();
- MutateRowsRequest clonedRequest = clonedMutation.toProto(REQUEST_CONTEXT);
+ MutateRowsRequest originalTableRequest = originalTableBulkMutation.toProto(REQUEST_CONTEXT);
+ BulkMutation clonedTableMutation = originalTableBulkMutation.clone();
+ MutateRowsRequest clonedTableRequest = clonedTableMutation.toProto(REQUEST_CONTEXT);
+
+ // Both BulkMutations should be equals.
+ assertThat(clonedTableRequest).isEqualTo(originalTableRequest);
+ assertThat(clonedTableRequest.getTableName()).isEqualTo(originalTableRequest.getTableName());
+ assertThat(clonedTableRequest.getAuthorizedViewName())
+ .isEqualTo(originalTableRequest.getAuthorizedViewName());
+ assertThat(clonedTableRequest.getEntriesList())
+ .isEqualTo(originalTableRequest.getEntriesList());
+
+ // Mutating cloned BulkMutation
+ clonedTableMutation.add(
+ "another-rowKey", Mutation.create().deleteCells("delete-family", "delete-qualifier"));
+ assertThat(clonedTableMutation.toProto(REQUEST_CONTEXT)).isNotEqualTo(originalTableRequest);
+
+ // Test BulkMutation on an authorized view.
+ BulkMutation originalAuthorizedViewBulkMutation =
+ BulkMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .add(
+ "test-rowKey",
+ Mutation.create().setCell("fake-family1", "fake-qualifier1", 12345, "fake-value1"));
+
+ MutateRowsRequest originalAuthorizedViewRequest =
+ originalAuthorizedViewBulkMutation.toProto(REQUEST_CONTEXT);
+ BulkMutation clonedAuthorizedViewMutation = originalAuthorizedViewBulkMutation.clone();
+ MutateRowsRequest clonedAuthorizedViewRequest =
+ clonedAuthorizedViewMutation.toProto(REQUEST_CONTEXT);
// Both BulkMutations should be equals.
- assertThat(clonedRequest).isEqualTo(originalRequest);
- assertThat(clonedRequest.getTableName()).isEqualTo(originalRequest.getTableName());
- assertThat(clonedRequest.getEntriesList()).isEqualTo(originalRequest.getEntriesList());
+ assertThat(clonedAuthorizedViewRequest).isEqualTo(originalAuthorizedViewRequest);
+ assertThat(clonedAuthorizedViewRequest.getTableName())
+ .isEqualTo(originalAuthorizedViewRequest.getTableName());
+ assertThat(clonedAuthorizedViewRequest.getAuthorizedViewName())
+ .isEqualTo(originalAuthorizedViewRequest.getAuthorizedViewName());
+ assertThat(clonedAuthorizedViewRequest.getEntriesList())
+ .isEqualTo(originalAuthorizedViewRequest.getEntriesList());
// Mutating cloned BulkMutation
- clonedMutation.add(
+ clonedAuthorizedViewMutation.add(
"another-rowKey", Mutation.create().deleteCells("delete-family", "delete-qualifier"));
- assertThat(clonedMutation.toProto(REQUEST_CONTEXT)).isNotEqualTo(originalRequest);
+ assertThat(clonedAuthorizedViewMutation.toProto(REQUEST_CONTEXT))
+ .isNotEqualTo(originalAuthorizedViewRequest);
+
+ // BulkMutations on an authorized view is different from BulkMutations on a table.
+ assertThat(originalAuthorizedViewRequest).isNotEqualTo(originalTableRequest);
+ assertThat(clonedAuthorizedViewRequest).isNotEqualTo(clonedTableRequest);
}
@Test
@@ -144,13 +223,21 @@ public void addRowMutationEntry() {
RowMutationEntry entry =
RowMutationEntry.create("test-rowKey")
.setCell("fake-family1", "fake-qualifier1", "fake-value1");
+
+ // Test BulkMutation on a table.
BulkMutation bulkMutation = BulkMutation.create(TABLE_ID);
bulkMutation.add(entry);
assertThat(bulkMutation.toProto(REQUEST_CONTEXT).getEntriesList()).contains(entry.toProto());
+
+ // Test BulkMutation on an authorized view.
+ bulkMutation = BulkMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+ bulkMutation.add(entry);
+ assertThat(bulkMutation.toProto(REQUEST_CONTEXT).getEntriesList()).contains(entry.toProto());
}
@Test
public void fromProtoTest() {
+ // Test BulkMutation on a table.
BulkMutation expected =
BulkMutation.create(TABLE_ID)
.add(
@@ -171,6 +258,29 @@ public void fromProtoTest() {
assertThat(overriddenRequest).isNotEqualTo(protoRequest);
assertThat(overriddenRequest.getTableName())
.matches(NameUtil.formatTableName(projectId, instanceId, TABLE_ID));
+ assertThat(overriddenRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+
+ // Test BulkMutation on an authorized view.
+ expected =
+ BulkMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .add(
+ "key",
+ Mutation.create().setCell("fake-family", "fake-qualifier", 10_000L, "fake-value"));
+
+ protoRequest = expected.toProto(REQUEST_CONTEXT);
+ actualBulkMutation = BulkMutation.fromProto(protoRequest);
+
+ assertThat(actualBulkMutation.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ overriddenRequest =
+ actualBulkMutation.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName()).isEmpty();
+ assertThat(overriddenRequest.getAuthorizedViewName())
+ .matches(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutationTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutationTest.java
index 8a626bb846..0f4e11a162 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutationTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutationTest.java
@@ -28,6 +28,8 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -37,6 +39,7 @@ public class ConditionalRowMutationTest {
private static final String PROJECT_ID = "fake-project";
private static final String INSTANCE_ID = "fake-instance";
private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String APP_PROFILE_ID = "fake-profile";
private static final RequestContext REQUEST_CONTEXT =
@@ -47,6 +50,8 @@ public class ConditionalRowMutationTest {
@Test
public void toProtoTest() {
Mutation ignoredThenMutation = Mutation.create().deleteRow();
+
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY).then(ignoredThenMutation);
@@ -60,10 +65,28 @@ public void toProtoTest() {
.setAppProfileId(APP_PROFILE_ID)
.setRowKey(TEST_KEY)
.build());
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .then(ignoredThenMutation);
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT).toBuilder().clearTrueMutations().build();
+
+ assertThat(actualProto)
+ .isEqualTo(
+ CheckAndMutateRowRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRowKey(TEST_KEY)
+ .build());
}
@Test
public void conditionTest() {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY)
.condition(Filters.FILTERS.key().regex("a.*"))
@@ -71,6 +94,18 @@ public void conditionTest() {
CheckAndMutateRowRequest actualProto = mutation.toProto(REQUEST_CONTEXT);
+ assertThat(actualProto.getPredicateFilter())
+ .isEqualTo(
+ RowFilter.newBuilder().setRowKeyRegexFilter(ByteString.copyFromUtf8("a.*")).build());
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .condition(Filters.FILTERS.key().regex("a.*"))
+ .then(Mutation.create().deleteRow());
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT);
+
assertThat(actualProto.getPredicateFilter())
.isEqualTo(
RowFilter.newBuilder().setRowKeyRegexFilter(ByteString.copyFromUtf8("a.*")).build());
@@ -78,6 +113,7 @@ public void conditionTest() {
@Test
public void thenTest() {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY)
.then(Mutation.create().deleteCells("family1", "qualifier1"))
@@ -85,25 +121,37 @@ public void thenTest() {
CheckAndMutateRowRequest actualProto = mutation.toProto(REQUEST_CONTEXT);
- assertThat(actualProto.getTrueMutationsList())
- .containsExactly(
- com.google.bigtable.v2.Mutation.newBuilder()
- .setDeleteFromColumn(
- DeleteFromColumn.newBuilder()
- .setFamilyName("family1")
- .setColumnQualifier(ByteString.copyFromUtf8("qualifier1")))
- .build(),
- com.google.bigtable.v2.Mutation.newBuilder()
- .setDeleteFromColumn(
- DeleteFromColumn.newBuilder()
- .setFamilyName("family2")
- .setColumnQualifier(ByteString.copyFromUtf8("qualifier2")))
- .build())
- .inOrder();
+ List expectedMutations = new ArrayList<>();
+ expectedMutations.add(
+ com.google.bigtable.v2.Mutation.newBuilder()
+ .setDeleteFromColumn(
+ DeleteFromColumn.newBuilder()
+ .setFamilyName("family1")
+ .setColumnQualifier(ByteString.copyFromUtf8("qualifier1")))
+ .build());
+ expectedMutations.add(
+ com.google.bigtable.v2.Mutation.newBuilder()
+ .setDeleteFromColumn(
+ DeleteFromColumn.newBuilder()
+ .setFamilyName("family2")
+ .setColumnQualifier(ByteString.copyFromUtf8("qualifier2")))
+ .build());
+ assertThat(actualProto.getTrueMutationsList()).isEqualTo(expectedMutations);
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .then(Mutation.create().deleteCells("family1", "qualifier1"))
+ .then(Mutation.create().deleteCells("family2", "qualifier2"));
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT);
+
+ assertThat(actualProto.getTrueMutationsList()).isEqualTo(expectedMutations);
}
@Test
public void otherwiseTest() {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY)
.otherwise(Mutation.create().deleteCells("family1", "qualifier1"))
@@ -111,25 +159,37 @@ public void otherwiseTest() {
CheckAndMutateRowRequest actualProto = mutation.toProto(REQUEST_CONTEXT);
- assertThat(actualProto.getFalseMutationsList())
- .containsExactly(
- com.google.bigtable.v2.Mutation.newBuilder()
- .setDeleteFromColumn(
- DeleteFromColumn.newBuilder()
- .setFamilyName("family1")
- .setColumnQualifier(ByteString.copyFromUtf8("qualifier1")))
- .build(),
- com.google.bigtable.v2.Mutation.newBuilder()
- .setDeleteFromColumn(
- DeleteFromColumn.newBuilder()
- .setFamilyName("family2")
- .setColumnQualifier(ByteString.copyFromUtf8("qualifier2")))
- .build())
- .inOrder();
+ List expectedMutations = new ArrayList<>();
+ expectedMutations.add(
+ com.google.bigtable.v2.Mutation.newBuilder()
+ .setDeleteFromColumn(
+ DeleteFromColumn.newBuilder()
+ .setFamilyName("family1")
+ .setColumnQualifier(ByteString.copyFromUtf8("qualifier1")))
+ .build());
+ expectedMutations.add(
+ com.google.bigtable.v2.Mutation.newBuilder()
+ .setDeleteFromColumn(
+ DeleteFromColumn.newBuilder()
+ .setFamilyName("family2")
+ .setColumnQualifier(ByteString.copyFromUtf8("qualifier2")))
+ .build());
+ assertThat(actualProto.getFalseMutationsList()).isEqualTo(expectedMutations);
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .otherwise(Mutation.create().deleteCells("family1", "qualifier1"))
+ .otherwise(Mutation.create().deleteCells("family2", "qualifier2"));
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT);
+
+ assertThat(actualProto.getFalseMutationsList()).isEqualTo(expectedMutations);
}
@Test
public void noEffectClausesTest() {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY).condition(Filters.FILTERS.pass());
@@ -142,10 +202,24 @@ public void noEffectClausesTest() {
}
assertThat(actualError).isInstanceOf(IllegalStateException.class);
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .condition(Filters.FILTERS.pass());
+
+ try {
+ mutation.toProto(REQUEST_CONTEXT);
+ } catch (Throwable t) {
+ actualError = t;
+ }
+
+ assertThat(actualError).isInstanceOf(IllegalStateException.class);
}
@Test
public void serializationTest() throws IOException, ClassNotFoundException {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation expected =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY)
.condition(Filters.FILTERS.pass())
@@ -161,10 +235,28 @@ public void serializationTest() throws IOException, ClassNotFoundException {
ConditionalRowMutation actual = (ConditionalRowMutation) ois.readObject();
assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
+
+ // Test ConditionalRowMutation on an authorized view.
+ expected =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .condition(Filters.FILTERS.pass())
+ .then(Mutation.create().deleteRow())
+ .otherwise(Mutation.create().deleteFamily("cf"));
+
+ bos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(expected);
+ oos.close();
+
+ ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+
+ actual = (ConditionalRowMutation) ois.readObject();
+ assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
}
@Test
public void fromProtoTest() {
+ // Test ConditionalRowMutation on a table.
ConditionalRowMutation mutation =
ConditionalRowMutation.create(TABLE_ID, TEST_KEY)
.condition(Filters.FILTERS.key().regex("test"))
@@ -185,6 +277,29 @@ public void fromProtoTest() {
assertThat(overriddenRequest).isNotEqualTo(protoRequest);
assertThat(overriddenRequest.getTableName())
.matches(NameUtil.formatTableName(projectId, instanceId, TABLE_ID));
+ assertThat(overriddenRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+
+ // Test ConditionalRowMutation on an authorized view.
+ mutation =
+ ConditionalRowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .condition(Filters.FILTERS.key().regex("test"))
+ .then(Mutation.create().setCell("family1", "qualifier1", 10_000L, "value"))
+ .otherwise(Mutation.create().deleteFamily("family"));
+
+ protoRequest = mutation.toProto(REQUEST_CONTEXT);
+ actualRequest = ConditionalRowMutation.fromProto(protoRequest);
+
+ assertThat(actualRequest.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ overriddenRequest =
+ actualRequest.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName()).isEmpty();
+ assertThat(overriddenRequest.getAuthorizedViewName())
+ .matches(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
}
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/QueryTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/QueryTest.java
index 93e5b1c92f..6ba80ed767 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/QueryTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/QueryTest.java
@@ -49,6 +49,7 @@ public class QueryTest {
private static final String PROJECT_ID = "fake-project";
private static final String INSTANCE_ID = "fake-instance";
private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String APP_PROFILE_ID = "fake-profile-id";
private RequestContext requestContext;
@@ -61,20 +62,26 @@ public void setUp() {
@Test
public void requestContextTest() {
+ // Table query test.
Query query = Query.create(TABLE_ID);
-
ReadRowsRequest proto = query.toProto(requestContext);
- assertThat(proto).isEqualTo(expectedProtoBuilder().build());
+ assertThat(proto).isEqualTo(expectedReadFromTableProtoBuilder().build());
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+ proto = query.toProto(requestContext);
+ assertThat(proto).isEqualTo(expectedReadFromAuthorizedViewProtoBuilder().build());
}
@Test
public void rowKeysTest() {
+ // Table query test.
Query query =
Query.create(TABLE_ID)
.rowKey("simple-string")
.rowKey(ByteString.copyFromUtf8("byte-string"));
- ReadRowsRequest.Builder expectedProto = expectedProtoBuilder();
+ ReadRowsRequest.Builder expectedProto = expectedReadFromTableProtoBuilder();
expectedProto
.getRowsBuilder()
.addRowKeys(ByteString.copyFromUtf8("simple-string"))
@@ -82,17 +89,33 @@ public void rowKeysTest() {
ReadRowsRequest actualProto = query.toProto(requestContext);
assertThat(actualProto).isEqualTo(expectedProto.build());
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .rowKey("simple-string")
+ .rowKey(ByteString.copyFromUtf8("byte-string"));
+
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder();
+ expectedProto
+ .getRowsBuilder()
+ .addRowKeys(ByteString.copyFromUtf8("simple-string"))
+ .addRowKeys(ByteString.copyFromUtf8("byte-string"));
+
+ actualProto = query.toProto(requestContext);
+ assertThat(actualProto).isEqualTo(expectedProto.build());
}
@Test
public void rowRangeTest() {
+ // Table query test.
Query query =
Query.create(TABLE_ID)
.range("simple-begin", "simple-end")
.range(ByteString.copyFromUtf8("byte-begin"), ByteString.copyFromUtf8("byte-end"))
.range(ByteStringRange.create("range-begin", "range-end"));
- Builder expectedProto = expectedProtoBuilder();
+ Builder expectedProto = expectedReadFromTableProtoBuilder();
expectedProto
.getRowsBuilder()
.addRowRanges(
@@ -110,10 +133,37 @@ public void rowRangeTest() {
ReadRowsRequest actualProto = query.toProto(requestContext);
assertThat(actualProto).isEqualTo(expectedProto.build());
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .range("simple-begin", "simple-end")
+ .range(ByteString.copyFromUtf8("byte-begin"), ByteString.copyFromUtf8("byte-end"))
+ .range(ByteStringRange.create("range-begin", "range-end"));
+
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder();
+ expectedProto
+ .getRowsBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("simple-begin"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("simple-end")))
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("byte-begin"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("byte-end")))
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("range-begin"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("range-end")));
+
+ actualProto = query.toProto(requestContext);
+ assertThat(actualProto).isEqualTo(expectedProto.build());
}
@Test
public void filterTestWithExceptions() {
+ // Table query test.
Exception actualException = null;
try {
Query.create(TABLE_ID).filter(null);
@@ -132,32 +182,73 @@ public void filterTestWithExceptions() {
actualException = ex;
}
assertThat(actualException).hasMessageThat().contains("filter size can't be more than 20KB");
+
+ // AuthorizedView query test.
+ actualException = null;
+ try {
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).filter(null);
+ } catch (Exception ex) {
+ actualException = ex;
+ }
+ assertThat(actualException).isInstanceOf(NullPointerException.class);
+
+ actualException = null;
+ try {
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.value().exactMatch(largeValue));
+ } catch (Exception ex) {
+ actualException = ex;
+ }
+ assertThat(actualException).hasMessageThat().contains("filter size can't be more than 20KB");
}
@Test
public void filterTest() {
+ // Table query test.
Query query = Query.create(TABLE_ID).filter(FILTERS.key().regex(".*"));
Builder expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
.setFilter(RowFilter.newBuilder().setRowKeyRegexFilter(ByteString.copyFromUtf8(".*")));
ReadRowsRequest actualProto = query.toProto(requestContext);
assertThat(actualProto).isEqualTo(expectedProto.build());
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.key().regex(".*"));
+
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
+ .setFilter(RowFilter.newBuilder().setRowKeyRegexFilter(ByteString.copyFromUtf8(".*")));
+
+ actualProto = query.toProto(requestContext);
+ assertThat(actualProto).isEqualTo(expectedProto.build());
}
@Test
public void limitTest() {
+ // Table query test.
Query query = Query.create(TABLE_ID).limit(10);
- Builder expectedProto = expectedProtoBuilder().setRowsLimit(10);
+ Builder expectedProto = expectedReadFromTableProtoBuilder().setRowsLimit(10);
ReadRowsRequest actualProto = query.toProto(requestContext);
assertThat(actualProto).isEqualTo(expectedProto.build());
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).limit(10);
+
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder().setRowsLimit(10);
+
+ actualProto = query.toProto(requestContext);
+ assertThat(actualProto).isEqualTo(expectedProto.build());
}
@Test
public void serializationTest() throws IOException, ClassNotFoundException {
+ // Table query test.
Query expected = Query.create(TABLE_ID).filter(FILTERS.key().regex(".*"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -169,10 +260,25 @@ public void serializationTest() throws IOException, ClassNotFoundException {
Query actual = (Query) ois.readObject();
assertThat(actual.toProto(requestContext)).isEqualTo(expected.toProto(requestContext));
+
+ // AuthorizedView query test.
+ expected =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.key().regex(".*"));
+
+ bos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(expected);
+ oos.close();
+
+ ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+ actual = (Query) ois.readObject();
+ assertThat(actual.toProto(requestContext)).isEqualTo(expected.toProto(requestContext));
}
@Test
public void shardTestSplitPoints() {
+ // Table query test.
Query query = Query.create(TABLE_ID).range("a", "z");
SortedSet splitPoints =
@@ -207,10 +313,46 @@ public void shardTestSplitPoints() {
.setStartKeyClosed(ByteString.copyFromUtf8("j"))
.setEndKeyOpen(ByteString.copyFromUtf8("z"))))
.build());
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).range("a", "z");
+
+ subQueries = query.shard(splitPoints);
+
+ assertThat(subQueries).hasSize(2);
+ assertThat(subQueries.get(0).toProto(requestContext))
+ .isEqualTo(
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("a"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("j"))))
+ .build());
+ assertThat(subQueries.get(1).toProto(requestContext))
+ .isEqualTo(
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("j"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))))
+ .build());
}
@Test
public void shardTestKeyOffsets() {
+ // Table query test.
Query query = Query.create(TABLE_ID).range("a", "z");
List keyOffsets =
@@ -245,16 +387,60 @@ public void shardTestKeyOffsets() {
.setStartKeyClosed(ByteString.copyFromUtf8("j"))
.setEndKeyOpen(ByteString.copyFromUtf8("z"))))
.build());
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).range("a", "z");
+
+ subQueries = query.shard(keyOffsets);
+
+ assertThat(subQueries).hasSize(2);
+ assertThat(subQueries.get(0).toProto(requestContext))
+ .isEqualTo(
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("a"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("j"))))
+ .build());
+ assertThat(subQueries.get(1).toProto(requestContext))
+ .isEqualTo(
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("j"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))))
+ .build());
}
- private static ReadRowsRequest.Builder expectedProtoBuilder() {
+ private static ReadRowsRequest.Builder expectedReadFromTableProtoBuilder() {
return ReadRowsRequest.newBuilder()
.setTableName(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID))
.setAppProfileId(APP_PROFILE_ID);
}
+ private static ReadRowsRequest.Builder expectedReadFromAuthorizedViewProtoBuilder() {
+ return ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID);
+ }
+
@Test
public void testFromProto() {
+ // Table query test.
ReadRowsRequest request =
ReadRowsRequest.newBuilder()
.setTableName(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID))
@@ -271,18 +457,76 @@ public void testFromProto() {
Query query = Query.fromProto(request);
assertThat(query.toProto(requestContext)).isEqualTo(request);
+
+ // AuthorizedView query test.
+ request =
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setFilter(RowFilter.newBuilder().setRowKeyRegexFilter(ByteString.copyFromUtf8(".*")))
+ .setRows(
+ RowSet.newBuilder()
+ .addRowKeys(ByteString.copyFromUtf8("row-key"))
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("j"))
+ .setEndKeyClosed(ByteString.copyFromUtf8("z"))))
+ .build();
+ query = Query.fromProto(request);
+
+ assertThat(query.toProto(requestContext)).isEqualTo(request);
}
@Test(expected = IllegalArgumentException.class)
- public void testFromProtoWithEmptyTableId() {
- Query.fromProto(ReadRowsRequest.getDefaultInstance());
+ public void testFromProtoWithInvalidTableId() {
+ Query.fromProto(
+ ReadRowsRequest.getDefaultInstance().toBuilder().setTableName("invalid-name").build());
expect.expect(IllegalArgumentException.class);
expect.expectMessage("Invalid table name:");
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithInvalidAuthorizedViewId() {
+ Query.fromProto(
+ ReadRowsRequest.getDefaultInstance()
+ .toBuilder()
+ .setAuthorizedViewName("invalid-name")
+ .build());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage("Invalid authorized view name:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithEmptyTableAndAuthorizedViewId() {
+ Query.fromProto(ReadRowsRequest.getDefaultInstance());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage("Either table name or authorized view name must be specified");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithBothTableAndAuthorizedViewId() {
+ Query.fromProto(
+ ReadRowsRequest.getDefaultInstance()
+ .toBuilder()
+ .setTableName(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID))
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .build());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage(
+ "Table name and authorized view name cannot be specified at the same time");
+ }
+
@Test
public void testEquality() {
+ // Table query test.
Query request =
Query.create(TABLE_ID)
.rowKey("row-key")
@@ -307,10 +551,47 @@ public void testEquality() {
assertThat(Query.create(TABLE_ID).filter(FILTERS.family().regex("test")))
.isNotEqualTo(Query.create(TABLE_ID).filter(FILTERS.family().exactMatch("test-one")));
assertThat(Query.create(TABLE_ID).limit(4)).isNotEqualTo(Query.create(TABLE_ID).limit(5));
+
+ // AuthorizedView query test.
+ request =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .rowKey("row-key")
+ .range("a", "z")
+ .limit(3)
+ .filter(FILTERS.family().exactMatch("test"));
+
+ // Query#toProto should not change the Query instance state
+ request.toProto(requestContext);
+ assertThat(request)
+ .isEqualTo(
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .rowKey("row-key")
+ .range("a", "z")
+ .limit(3)
+ .filter(FILTERS.family().exactMatch("test")));
+
+ assertThat(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).rowKey("row-key"))
+ .isNotEqualTo(
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).rowKey("row-key-1"));
+ assertThat(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).range("a", "z"))
+ .isNotEqualTo(
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).range("a", "s"));
+ assertThat(
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.family().regex("test")))
+ .isNotEqualTo(
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.family().exactMatch("test-one")));
+ assertThat(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).limit(4))
+ .isNotEqualTo(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).limit(5));
+
+ assertThat(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)))
+ .isNotEqualTo(Query.create(TABLE_ID));
}
@Test
public void testClone() {
+ // Table query test.
Query query = Query.create(TABLE_ID).filter(FILTERS.key().regex("temp")).limit(10);
ReadRowsRequest request =
ReadRowsRequest.newBuilder()
@@ -326,10 +607,33 @@ public void testClone() {
Query clonedReq = query.clone();
assertThat(clonedReq).isEqualTo(query);
assertThat(clonedReq.toProto(requestContext)).isEqualTo(request);
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .filter(FILTERS.key().regex("temp"))
+ .limit(10);
+ request =
+ ReadRowsRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRowsLimit(10)
+ .setFilter(
+ RowFilter.newBuilder()
+ .setRowKeyRegexFilter(ByteString.copyFromUtf8("temp"))
+ .build())
+ .build();
+
+ clonedReq = query.clone();
+ assertThat(clonedReq).isEqualTo(query);
+ assertThat(clonedReq.toProto(requestContext)).isEqualTo(request);
}
@Test
public void testQueryPaginatorRangeLimitReached() {
+ // Table query test.
int chunkSize = 10, limit = 15;
Query query = Query.create(TABLE_ID).range("a", "z").limit(limit);
Query.QueryPaginator paginator = query.createPaginator(chunkSize);
@@ -337,7 +641,7 @@ public void testQueryPaginatorRangeLimitReached() {
Query nextQuery = paginator.getNextQuery();
Builder expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
.setRows(
RowSet.newBuilder()
.addRowRanges(
@@ -352,7 +656,44 @@ public void testQueryPaginatorRangeLimitReached() {
int expectedLimit = limit - chunkSize;
nextQuery = paginator.getNextQuery();
expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyOpen(ByteString.copyFromUtf8("c"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(expectedLimit);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("d"))).isFalse();
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .range("a", "z")
+ .limit(limit);
+ paginator = query.createPaginator(chunkSize);
+
+ nextQuery = paginator.getNextQuery();
+
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("a"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(chunkSize);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("c"))).isTrue();
+ nextQuery = paginator.getNextQuery();
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
.setRows(
RowSet.newBuilder()
.addRowRanges(
@@ -369,13 +710,15 @@ public void testQueryPaginatorRangeLimitReached() {
@Test
public void testQueryPaginatorRangeLimitMultiplyOfChunkSize() {
int chunkSize = 10, limit = 20;
+
+ // Table query test.
Query query = Query.create(TABLE_ID).range("a", "z").limit(limit);
Query.QueryPaginator paginator = query.createPaginator(chunkSize);
Query nextQuery = paginator.getNextQuery();
Builder expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
.setRows(
RowSet.newBuilder()
.addRowRanges(
@@ -390,7 +733,44 @@ public void testQueryPaginatorRangeLimitMultiplyOfChunkSize() {
int expectedLimit = limit - chunkSize;
nextQuery = paginator.getNextQuery();
expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyOpen(ByteString.copyFromUtf8("c"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(expectedLimit);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("d"))).isFalse();
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .range("a", "z")
+ .limit(limit);
+ paginator = query.createPaginator(chunkSize);
+
+ nextQuery = paginator.getNextQuery();
+
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("a"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(chunkSize);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("c"))).isTrue();
+ nextQuery = paginator.getNextQuery();
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
.setRows(
RowSet.newBuilder()
.addRowRanges(
@@ -407,13 +787,48 @@ public void testQueryPaginatorRangeLimitMultiplyOfChunkSize() {
@Test
public void testQueryPaginatorRagneNoLimit() {
int chunkSize = 10;
+
+ // Table query test.
Query query = Query.create(TABLE_ID).range("a", "z");
Query.QueryPaginator paginator = query.createPaginator(chunkSize);
Query nextQuery = paginator.getNextQuery();
Builder expectedProto =
- expectedProtoBuilder()
+ expectedReadFromTableProtoBuilder()
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyClosed(ByteString.copyFromUtf8("a"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(chunkSize);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("c"))).isTrue();
+ nextQuery = paginator.getNextQuery();
+ expectedProto
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder()
+ .setStartKeyOpen(ByteString.copyFromUtf8("c"))
+ .setEndKeyOpen(ByteString.copyFromUtf8("z"))
+ .build()))
+ .setRowsLimit(chunkSize);
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("z"))).isFalse();
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)).range("a", "z");
+ paginator = query.createPaginator(chunkSize);
+
+ nextQuery = paginator.getNextQuery();
+
+ expectedProto =
+ expectedReadFromAuthorizedViewProtoBuilder()
.setRows(
RowSet.newBuilder()
.addRowRanges(
@@ -443,13 +858,15 @@ public void testQueryPaginatorRagneNoLimit() {
@Test
public void testQueryPaginatorRowsNoLimit() {
int chunkSize = 10;
+
+ // Table query test.
Query query = Query.create(TABLE_ID).rowKey("a").rowKey("b").rowKey("c");
Query.QueryPaginator paginator = query.createPaginator(chunkSize);
Query nextQuery = paginator.getNextQuery();
- ReadRowsRequest.Builder expectedProto = expectedProtoBuilder();
+ ReadRowsRequest.Builder expectedProto = expectedReadFromTableProtoBuilder();
expectedProto
.getRowsBuilder()
.addRowKeys(ByteString.copyFromUtf8("a"))
@@ -461,7 +878,38 @@ public void testQueryPaginatorRowsNoLimit() {
paginator.advance(ByteString.copyFromUtf8("b"));
nextQuery = paginator.getNextQuery();
- expectedProto = expectedProtoBuilder();
+ expectedProto = expectedReadFromTableProtoBuilder();
+ expectedProto.getRowsBuilder().addRowKeys(ByteString.copyFromUtf8("c"));
+ expectedProto.setRowsLimit(chunkSize);
+
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ assertThat(paginator.advance(ByteString.copyFromUtf8("c"))).isFalse();
+
+ // AuthorizedView query test.
+ query =
+ Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID))
+ .rowKey("a")
+ .rowKey("b")
+ .rowKey("c");
+
+ paginator = query.createPaginator(chunkSize);
+
+ nextQuery = paginator.getNextQuery();
+
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder();
+ expectedProto
+ .getRowsBuilder()
+ .addRowKeys(ByteString.copyFromUtf8("a"))
+ .addRowKeys(ByteString.copyFromUtf8("b"))
+ .addRowKeys(ByteString.copyFromUtf8("c"));
+ expectedProto.setRowsLimit(chunkSize);
+
+ assertThat(nextQuery.toProto(requestContext)).isEqualTo(expectedProto.build());
+
+ paginator.advance(ByteString.copyFromUtf8("b"));
+ nextQuery = paginator.getNextQuery();
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder();
expectedProto.getRowsBuilder().addRowKeys(ByteString.copyFromUtf8("c"));
expectedProto.setRowsLimit(chunkSize);
@@ -473,10 +921,33 @@ public void testQueryPaginatorRowsNoLimit() {
@Test
public void testQueryPaginatorFullTableScan() {
int chunkSize = 10;
+
+ // Table query test.
Query query = Query.create(TABLE_ID);
Query.QueryPaginator queryPaginator = query.createPaginator(chunkSize);
- ReadRowsRequest.Builder expectedProto = expectedProtoBuilder().setRowsLimit(chunkSize);
+ ReadRowsRequest.Builder expectedProto =
+ expectedReadFromTableProtoBuilder().setRowsLimit(chunkSize);
+ assertThat(queryPaginator.getNextQuery().toProto(requestContext))
+ .isEqualTo(expectedProto.build());
+
+ assertThat(queryPaginator.advance(ByteString.copyFromUtf8("a"))).isTrue();
+ expectedProto
+ .setRows(
+ RowSet.newBuilder()
+ .addRowRanges(
+ RowRange.newBuilder().setStartKeyOpen(ByteString.copyFromUtf8("a")).build()))
+ .setRowsLimit(chunkSize);
+ assertThat(queryPaginator.getNextQuery().toProto(requestContext))
+ .isEqualTo(expectedProto.build());
+
+ assertThat(queryPaginator.advance(ByteString.copyFromUtf8("a"))).isFalse();
+
+ // AuthorizedView query test.
+ query = Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+ queryPaginator = query.createPaginator(chunkSize);
+
+ expectedProto = expectedReadFromAuthorizedViewProtoBuilder().setRowsLimit(chunkSize);
assertThat(queryPaginator.getNextQuery().toProto(requestContext))
.isEqualTo(expectedProto.build());
@@ -499,7 +970,8 @@ public void testQueryPaginatorEmptyTable() {
Query query = Query.create(TABLE_ID);
Query.QueryPaginator queryPaginator = query.createPaginator(chunkSize);
- ReadRowsRequest.Builder expectedProto = expectedProtoBuilder().setRowsLimit(chunkSize);
+ ReadRowsRequest.Builder expectedProto =
+ expectedReadFromTableProtoBuilder().setRowsLimit(chunkSize);
assertThat(queryPaginator.getNextQuery().toProto(requestContext))
.isEqualTo(expectedProto.build());
@@ -510,6 +982,6 @@ public void testQueryPaginatorEmptyTable() {
public void testQueryReversed() {
Query query = Query.create(TABLE_ID).reversed(true);
assertThat(query.toProto(requestContext))
- .isEqualTo(expectedProtoBuilder().setReversed(true).build());
+ .isEqualTo(expectedReadFromTableProtoBuilder().setReversed(true).build());
}
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRowTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRowTest.java
index b0a8be33c9..90a8c6c1de 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRowTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRowTest.java
@@ -36,12 +36,14 @@ public class ReadModifyWriteRowTest {
private static final String PROJECT_ID = "fake-project";
private static final String INSTANCE_ID = "fake-instance";
private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String APP_PROFILE_ID = "fake-profile";
private static final RequestContext REQUEST_CONTEXT =
RequestContext.create(PROJECT_ID, INSTANCE_ID, APP_PROFILE_ID);
@Test
public void testAppend() {
+ // Test ReadModifyWriteRow on a table.
ReadModifyWriteRow mutation =
ReadModifyWriteRow.create(TABLE_ID, "fake-key")
.append(
@@ -69,10 +71,42 @@ public void testAppend() {
.setAppendValue(ByteString.copyFromUtf8("fake-value-str")))
.build();
assertThat(actualProto).isEqualTo(expected);
+
+ // Test ReadModifyWriteRow on an authorized view.
+ mutation =
+ ReadModifyWriteRow.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), "fake-key")
+ .append(
+ "fake-family",
+ ByteString.copyFromUtf8("fake-qualifier"),
+ ByteString.copyFromUtf8("fake-value"))
+ .append("fake-family", "fake-qualifier-str", "fake-value-str");
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT);
+
+ expected =
+ ReadModifyWriteRowRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRowKey(ByteString.copyFromUtf8("fake-key"))
+ .addRules(
+ ReadModifyWriteRule.newBuilder()
+ .setFamilyName("fake-family")
+ .setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier"))
+ .setAppendValue(ByteString.copyFromUtf8("fake-value")))
+ .addRules(
+ ReadModifyWriteRule.newBuilder()
+ .setFamilyName("fake-family")
+ .setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier-str"))
+ .setAppendValue(ByteString.copyFromUtf8("fake-value-str")))
+ .build();
+ assertThat(actualProto).isEqualTo(expected);
}
@Test
public void testIncrement() {
+ // Test ReadModifyWriteRow on a table.
ReadModifyWriteRow mutation =
ReadModifyWriteRow.create(TABLE_ID, "fake-key")
.increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
@@ -97,10 +131,39 @@ public void testIncrement() {
.setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier-str"))
.setIncrementAmount(2))
.build());
+
+ // Test ReadModifyWriteRow on an authorized view.
+ mutation =
+ ReadModifyWriteRow.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), "fake-key")
+ .increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
+ .increment("fake-family", "fake-qualifier-str", 2);
+
+ actualProto = mutation.toProto(REQUEST_CONTEXT);
+
+ assertThat(actualProto)
+ .isEqualTo(
+ ReadModifyWriteRowRequest.newBuilder()
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .setAppProfileId(APP_PROFILE_ID)
+ .setRowKey(ByteString.copyFromUtf8("fake-key"))
+ .addRules(
+ ReadModifyWriteRule.newBuilder()
+ .setFamilyName("fake-family")
+ .setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier"))
+ .setIncrementAmount(1))
+ .addRules(
+ ReadModifyWriteRule.newBuilder()
+ .setFamilyName("fake-family")
+ .setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier-str"))
+ .setIncrementAmount(2))
+ .build());
}
@Test
public void serializationTest() throws IOException, ClassNotFoundException {
+ // Test ReadModifyWriteRow on a table.
ReadModifyWriteRow expected =
ReadModifyWriteRow.create(TABLE_ID, "fake-key")
.increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
@@ -115,10 +178,27 @@ public void serializationTest() throws IOException, ClassNotFoundException {
ReadModifyWriteRow actual = (ReadModifyWriteRow) ois.readObject();
assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
+
+ // Test ReadModifyWriteRow on an authorized view.
+ expected =
+ ReadModifyWriteRow.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), "fake-key")
+ .increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
+ .append("fake-family", "a", "b");
+
+ bos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(expected);
+ oos.close();
+
+ ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+
+ actual = (ReadModifyWriteRow) ois.readObject();
+ assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
}
@Test
public void fromProtoTest() {
+ // Test ReadModifyWriteRow on a table.
ReadModifyWriteRow expected =
ReadModifyWriteRow.create(TABLE_ID, "row-key")
.increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
@@ -138,6 +218,28 @@ public void fromProtoTest() {
assertThat(overriddenRequest).isNotEqualTo(protoRequest);
assertThat(overriddenRequest.getTableName())
.matches(NameUtil.formatTableName(projectId, instanceId, TABLE_ID));
+ assertThat(overriddenRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+
+ // Test ReadModifyWriteRow on an authorized view.
+ expected =
+ ReadModifyWriteRow.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), "row-key")
+ .increment("fake-family", ByteString.copyFromUtf8("fake-qualifier"), 1)
+ .append("fake-family", "fake-qualifier", "fake-value");
+
+ protoRequest = expected.toProto(REQUEST_CONTEXT);
+ actualRequest = ReadModifyWriteRow.fromProto(protoRequest);
+
+ assertThat(actualRequest.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ overriddenRequest =
+ actualRequest.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName()).isEmpty();
+ assertThat(overriddenRequest.getAuthorizedViewName())
+ .matches(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
}
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java
index b401ad5ef3..2e59c56336 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/RowMutationTest.java
@@ -38,16 +38,19 @@ public class RowMutationTest {
private static final String PROJECT_ID = "fake-project";
private static final String INSTANCE_ID = "fake-instance";
private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String APP_PROFILE_ID = "fake-profile";
private static final RequestContext REQUEST_CONTEXT =
RequestContext.create(PROJECT_ID, INSTANCE_ID, APP_PROFILE_ID);
+ private static final ByteString TEST_KEY = ByteString.copyFromUtf8("fake-key");
@Test
public void toProtoTest() {
long timestampMin = System.currentTimeMillis() * 1_000;
+ // Test RowMutation on a table.
RowMutation rowMutation =
- RowMutation.create("fake-table", "fake-key")
+ RowMutation.create(TABLE_ID, TEST_KEY)
.setCell("fake-family", "fake-qualifier", "fake-value");
MutateRowRequest actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT);
@@ -55,7 +58,29 @@ public void toProtoTest() {
com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000);
assertThat(actualRowMutation.getTableName())
- .isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, "fake-table"));
+ .isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID));
+ assertThat(actualRowMutation.getAuthorizedViewName()).isEmpty();
+ assertThat(actualRowMutation.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
+ assertThat(actualRowMutation.getMutationsList()).hasSize(1);
+ assertThat(actualRowMutation.getMutations(0).getSetCell().getValue())
+ .isEqualTo(ByteString.copyFromUtf8("fake-value"));
+ assertThat(actualRowMutation.getMutations(0).getSetCell().getTimestampMicros())
+ .isIn(timestampRange);
+
+ // Test RowMutation on an authorized view.
+ rowMutation =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .setCell("fake-family", "fake-qualifier", "fake-value");
+
+ actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT);
+ timestampRange =
+ com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000);
+
+ assertThat(actualRowMutation.getTableName()).isEmpty();
+ assertThat(actualRowMutation.getAuthorizedViewName())
+ .isEqualTo(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(actualRowMutation.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
assertThat(actualRowMutation.getMutationsList()).hasSize(1);
assertThat(actualRowMutation.getMutations(0).getSetCell().getValue())
@@ -68,8 +93,9 @@ public void toProtoTest() {
public void toBulkProtoTest() {
long timestampMin = System.currentTimeMillis() * 1_000;
+ // Test RowMutation on a table.
RowMutation rowMutation =
- RowMutation.create("fake-table", "fake-key")
+ RowMutation.create(TABLE_ID, TEST_KEY)
.setCell("fake-family", "fake-qualifier", "fake-value");
MutateRowsRequest actualRowMutation = rowMutation.toBulkProto(REQUEST_CONTEXT);
@@ -79,6 +105,31 @@ public void toBulkProtoTest() {
assertThat(actualRowMutation.getTableName())
.isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID));
+ assertThat(actualRowMutation.getAuthorizedViewName()).isEmpty();
+ assertThat(actualRowMutation.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
+ assertThat(actualRowMutation.getEntriesList()).hasSize(1);
+ assertThat(actualRowMutation.getEntries(0).getMutationsList()).hasSize(1);
+ assertThat(actualRowMutation.getEntries(0).getMutations(0).getSetCell().getValue())
+ .isEqualTo(ByteString.copyFromUtf8("fake-value"));
+
+ assertThat(actualRowMutation.getEntries(0).getMutations(0).getSetCell().getTimestampMicros())
+ .isIn(timestampRange);
+
+ // Test RowMutation on an authorized view.
+ rowMutation =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .setCell("fake-family", "fake-qualifier", "fake-value");
+
+ actualRowMutation = rowMutation.toBulkProto(REQUEST_CONTEXT);
+
+ timestampRange =
+ com.google.common.collect.Range.closed(timestampMin, System.currentTimeMillis() * 1_000);
+
+ assertThat(actualRowMutation.getTableName()).isEmpty();
+ assertThat(actualRowMutation.getAuthorizedViewName())
+ .isEqualTo(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(actualRowMutation.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
assertThat(actualRowMutation.getEntriesList()).hasSize(1);
assertThat(actualRowMutation.getEntries(0).getMutationsList()).hasSize(1);
@@ -92,17 +143,27 @@ public void toBulkProtoTest() {
@Test
public void toProtoTestWithProvidedMutation() {
Mutation mutation = Mutation.create().setCell("fake-family", "fake-qualifier", "fake-value");
- RowMutation rowMutation = RowMutation.create("fake-table", "fake-key", mutation);
+ // Test RowMutation on a table.
+ RowMutation rowMutation = RowMutation.create(TABLE_ID, TEST_KEY, mutation);
MutateRowRequest actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT);
assertThat(actualRowMutation.getMutationsList()).isEqualTo(mutation.getMutations());
+
+ // Test RowMutation on an authorized view.
+ rowMutation =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY, mutation);
+
+ actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT);
+
+ assertThat(actualRowMutation.getMutationsList()).isEqualTo(mutation.getMutations());
}
@Test
public void serializationTest() throws IOException, ClassNotFoundException {
+ // Test RowMutation on a table.
RowMutation expected =
- RowMutation.create("fake-table", "fake-key")
+ RowMutation.create(TABLE_ID, TEST_KEY)
.setCell("fake-family", "fake-qualifier", 10_000, "fake-value");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -114,12 +175,28 @@ public void serializationTest() throws IOException, ClassNotFoundException {
RowMutation actual = (RowMutation) ois.readObject();
assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
+
+ // Test RowMutation on an authorized view.
+ expected =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .setCell("fake-family", "fake-qualifier", 10_000, "fake-value");
+
+ bos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(bos);
+ oos.writeObject(expected);
+ oos.close();
+
+ ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+
+ actual = (RowMutation) ois.readObject();
+ assertThat(actual.toProto(REQUEST_CONTEXT)).isEqualTo(expected.toProto(REQUEST_CONTEXT));
}
@Test
public void testWithLongValue() {
+ // Test RowMutation on a table.
RowMutation rowMutation =
- RowMutation.create("fake-table", "fake-key")
+ RowMutation.create(TABLE_ID, TEST_KEY)
.setCell("fake-family", "fake-qualifier", 100_000L)
.setCell("fake-family", "fake-qualifier", 30_000L, 100_000L);
@@ -130,6 +207,28 @@ public void testWithLongValue() {
assertThat(setCell.getColumnQualifier().toStringUtf8()).isEqualTo("fake-qualifier");
assertThat(setCell.getValue()).isEqualTo(ByteString.copyFrom(Longs.toByteArray(100_000L)));
+ assertThat(actualRowMutation.getMutations(1).getSetCell())
+ .isEqualTo(
+ SetCell.newBuilder()
+ .setFamilyName("fake-family")
+ .setColumnQualifier(ByteString.copyFromUtf8("fake-qualifier"))
+ .setTimestampMicros(30_000L)
+ .setValue(ByteString.copyFrom(Longs.toByteArray(100_000L)))
+ .build());
+
+ // Test RowMutation on an authorized view.
+ rowMutation =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .setCell("fake-family", "fake-qualifier", 100_000L)
+ .setCell("fake-family", "fake-qualifier", 30_000L, 100_000L);
+
+ actualRowMutation = rowMutation.toProto(REQUEST_CONTEXT);
+
+ setCell = actualRowMutation.getMutations(0).getSetCell();
+ assertThat(setCell.getFamilyName()).isEqualTo("fake-family");
+ assertThat(setCell.getColumnQualifier().toStringUtf8()).isEqualTo("fake-qualifier");
+ assertThat(setCell.getValue()).isEqualTo(ByteString.copyFrom(Longs.toByteArray(100_000L)));
+
assertThat(actualRowMutation.getMutations(1).getSetCell())
.isEqualTo(
SetCell.newBuilder()
@@ -142,8 +241,9 @@ public void testWithLongValue() {
@Test
public void fromProtoTest() {
+ // Test RowMutation on a table.
RowMutation rowMutation =
- RowMutation.create("fake-table", "fake-key")
+ RowMutation.create(TABLE_ID, TEST_KEY)
.setCell("fake-family", "fake-qualifier-1", "fake-value")
.setCell("fake-family", "fake-qualifier-2", 30_000L, "fake-value-2");
@@ -161,6 +261,28 @@ public void fromProtoTest() {
assertThat(overriddenRequest).isNotEqualTo(protoRequest);
assertThat(overriddenRequest.getTableName())
.matches(NameUtil.formatTableName(projectId, instanceId, TABLE_ID));
+ assertThat(overriddenRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+
+ // Test RowMutation on an authorized view.
+ rowMutation =
+ RowMutation.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID), TEST_KEY)
+ .setCell("fake-family", "fake-qualifier-1", "fake-value")
+ .setCell("fake-family", "fake-qualifier-2", 30_000L, "fake-value-2");
+
+ protoRequest = rowMutation.toProto(REQUEST_CONTEXT);
+ actualRequest = RowMutation.fromProto(protoRequest);
+
+ assertThat(actualRequest.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ overriddenRequest =
+ actualRequest.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName()).isEmpty();
+ assertThat(overriddenRequest.getAuthorizedViewName())
+ .matches(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, TABLE_ID, AUTHORIZED_VIEW_ID));
assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
}
}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequestTest.java
new file mode 100644
index 0000000000..4aa8a2b809
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequestTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.data.v2.models;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SampleRowKeysRequestTest {
+ private static final String PROJECT_ID = "fake-project";
+ private static final String INSTANCE_ID = "fake-instance";
+ private static final String TABLE_ID = "fake-table";
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
+ private static final String APP_PROFILE_ID = "fake-profile";
+ private static final RequestContext REQUEST_CONTEXT =
+ RequestContext.create(PROJECT_ID, INSTANCE_ID, APP_PROFILE_ID);
+ @Rule public ExpectedException expect = ExpectedException.none();
+
+ @Test
+ public void toProtoTest() {
+ // Test SampleRowKeysRequest on a table.
+ SampleRowKeysRequest sampleRowKeysRequest = SampleRowKeysRequest.create(TableId.of(TABLE_ID));
+ com.google.bigtable.v2.SampleRowKeysRequest actualRequest =
+ sampleRowKeysRequest.toProto(REQUEST_CONTEXT);
+ assertThat(actualRequest.getTableName())
+ .isEqualTo(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID));
+ assertThat(actualRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(actualRequest.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
+
+ // Test SampleRowKeysRequest on an authorized view.
+ sampleRowKeysRequest =
+ SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+ actualRequest = sampleRowKeysRequest.toProto(REQUEST_CONTEXT);
+ assertThat(actualRequest.getTableName()).isEmpty();
+ assertThat(actualRequest.getAuthorizedViewName())
+ .isEqualTo(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID));
+ assertThat(actualRequest.getAppProfileId()).isEqualTo(APP_PROFILE_ID);
+ }
+
+ @Test
+ public void fromProtoTest() {
+ // Test SampleRowKeysRequest on a table.
+ SampleRowKeysRequest sampleRowKeysRequest = SampleRowKeysRequest.create(TableId.of(TABLE_ID));
+
+ com.google.bigtable.v2.SampleRowKeysRequest protoRequest =
+ sampleRowKeysRequest.toProto(REQUEST_CONTEXT);
+ SampleRowKeysRequest actualRequest = SampleRowKeysRequest.fromProto(protoRequest);
+
+ assertThat(actualRequest.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ String projectId = "fresh-project";
+ String instanceId = "fresh-instance";
+ String appProfile = "fresh-app-profile";
+ com.google.bigtable.v2.SampleRowKeysRequest overriddenRequest =
+ actualRequest.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName())
+ .matches(NameUtil.formatTableName(projectId, instanceId, TABLE_ID));
+ assertThat(overriddenRequest.getAuthorizedViewName()).isEmpty();
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+
+ // Test SampleRowKeysRequest on an authorized view.
+ sampleRowKeysRequest =
+ SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID));
+
+ protoRequest = sampleRowKeysRequest.toProto(REQUEST_CONTEXT);
+ actualRequest = SampleRowKeysRequest.fromProto(protoRequest);
+
+ assertThat(actualRequest.toProto(REQUEST_CONTEXT)).isEqualTo(protoRequest);
+
+ overriddenRequest =
+ actualRequest.toProto(RequestContext.create(projectId, instanceId, appProfile));
+
+ assertThat(overriddenRequest).isNotEqualTo(protoRequest);
+ assertThat(overriddenRequest.getTableName()).isEmpty();
+ assertThat(overriddenRequest.getAuthorizedViewName())
+ .matches(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, TABLE_ID, AUTHORIZED_VIEW_ID));
+ assertThat(overriddenRequest.getAppProfileId()).matches(appProfile);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithInvalidTableId() {
+ SampleRowKeysRequest.fromProto(
+ com.google.bigtable.v2.SampleRowKeysRequest.getDefaultInstance()
+ .toBuilder()
+ .setTableName("invalid-name")
+ .build());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage("Invalid table name:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithInvalidAuthorizedViewId() {
+ SampleRowKeysRequest.fromProto(
+ com.google.bigtable.v2.SampleRowKeysRequest.getDefaultInstance()
+ .toBuilder()
+ .setAuthorizedViewName("invalid-name")
+ .build());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage("Invalid authorized view name:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithEmptyTableAndAuthorizedViewId() {
+ SampleRowKeysRequest.fromProto(
+ com.google.bigtable.v2.SampleRowKeysRequest.getDefaultInstance());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage("Either table name or authorized view name must be specified");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFromProtoWithBothTableAndAuthorizedViewId() {
+ SampleRowKeysRequest.fromProto(
+ com.google.bigtable.v2.SampleRowKeysRequest.getDefaultInstance()
+ .toBuilder()
+ .setTableName(NameUtil.formatTableName(PROJECT_ID, INSTANCE_ID, TABLE_ID))
+ .setAuthorizedViewName(
+ NameUtil.formatAuthorizedViewName(
+ PROJECT_ID, INSTANCE_ID, TABLE_ID, AUTHORIZED_VIEW_ID))
+ .build());
+
+ expect.expect(IllegalArgumentException.class);
+ expect.expectMessage(
+ "Table name and authorized view name cannot be specified at the same time");
+ }
+
+ @Test
+ public void testEquality() {
+ // Test SampleRowKeysRequest on a table.
+ assertThat(SampleRowKeysRequest.create(TableId.of(TABLE_ID)))
+ .isEqualTo(SampleRowKeysRequest.create(TableId.of(TABLE_ID)));
+ assertThat(SampleRowKeysRequest.create(TableId.of("another-table")))
+ .isNotEqualTo(SampleRowKeysRequest.create(TableId.of(TABLE_ID)));
+
+ // Test SampleRowKeysRequest on an authorized view.
+ assertThat(SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)))
+ .isEqualTo(SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)));
+ assertThat(
+ SampleRowKeysRequest.create(AuthorizedViewId.of("another-table", AUTHORIZED_VIEW_ID)))
+ .isNotEqualTo(
+ SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)));
+ assertThat(
+ SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, "another-authorized-view")))
+ .isNotEqualTo(
+ SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)));
+
+ assertThat(SampleRowKeysRequest.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)))
+ .isNotEqualTo(SampleRowKeysRequest.create(TableId.of(TABLE_ID)));
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/TableIdTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/TableIdTest.java
new file mode 100644
index 0000000000..8e1c9d502d
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/models/TableIdTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.data.v2.models;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TableIdTest {
+ private static final String PROJECT_ID = "my-project";
+ private static final String INSTANCE_ID = "my-instance";
+ private static final String TABLE_ID = "my-table";
+
+ @Test
+ public void testToResourceName() {
+ TableId tableId = TableId.of(TABLE_ID);
+
+ assertThat(tableId.toResourceName(PROJECT_ID, INSTANCE_ID))
+ .isEqualTo("projects/my-project/instances/my-instance/tables/my-table");
+ }
+
+ @Test
+ public void testEquality() {
+ TableId tableId = TableId.of(TABLE_ID);
+
+ assertThat(tableId).isEqualTo(TableId.of(TABLE_ID));
+ assertThat(tableId).isNotEqualTo(TableId.of("another-table"));
+ }
+
+ @Test
+ public void testHashCode() {
+ TableId tableId = TableId.of(TABLE_ID);
+
+ assertThat(tableId.hashCode()).isEqualTo(TableId.of(TABLE_ID).hashCode());
+ assertThat(tableId.hashCode()).isNotEqualTo(TableId.of("another-table").hashCode());
+ }
+
+ @Test
+ public void testToString() {
+ TableId tableId = TableId.of(TABLE_ID);
+
+ assertThat(tableId.toString()).isEqualTo("TableId{tableId=my-table}");
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequestTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequestTest.java
new file mode 100644
index 0000000000..f974076ceb
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequestTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.data.v2.stub;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.api.core.ApiFuture;
+import com.google.api.core.SettableApiFuture;
+import com.google.api.gax.grpc.GrpcStatusCode;
+import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.NotFoundException;
+import com.google.api.gax.rpc.UnaryCallable;
+import com.google.bigtable.v2.SampleRowKeysResponse;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import com.google.cloud.bigtable.data.v2.models.KeyOffset;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TableId;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+import io.grpc.Status.Code;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SampleRowKeysCallableWithRequestTest {
+
+ private final RequestContext requestContext =
+ RequestContext.create("my-project", "my-instance", "my-profile");
+ private FakeCallable inner;
+ private SampleRowKeysCallableWithRequest callable;
+
+ @Before
+ public void setUp() {
+ inner = new FakeCallable();
+ callable = new SampleRowKeysCallableWithRequest(inner, requestContext);
+ }
+
+ @Test
+ public void requestIsCorrect() {
+ callable.futureCall(SampleRowKeysRequest.create(TableId.of("my-table")));
+
+ assertThat(inner.request)
+ .isEqualTo(
+ com.google.bigtable.v2.SampleRowKeysRequest.newBuilder()
+ .setTableName(
+ NameUtil.formatTableName(
+ requestContext.getProjectId(), requestContext.getInstanceId(), "my-table"))
+ .setAppProfileId(requestContext.getAppProfileId())
+ .build());
+ }
+
+ @Test
+ public void responseCorrectlyTransformed() throws Exception {
+ ApiFuture> result =
+ callable.futureCall(SampleRowKeysRequest.create(TableId.of("my-table")));
+
+ inner.response.set(
+ ImmutableList.of(
+ SampleRowKeysResponse.newBuilder()
+ .setRowKey(ByteString.copyFromUtf8("key1"))
+ .setOffsetBytes(100)
+ .build(),
+ SampleRowKeysResponse.newBuilder()
+ .setRowKey(ByteString.copyFromUtf8(""))
+ .setOffsetBytes(1000)
+ .build()));
+
+ assertThat(result.get(1, TimeUnit.SECONDS))
+ .isEqualTo(
+ ImmutableList.of(
+ KeyOffset.create(ByteString.copyFromUtf8("key1"), 100),
+ KeyOffset.create(ByteString.EMPTY, 1000)));
+ }
+
+ @Test
+ public void errorIsPropagated() throws Exception {
+ ApiFuture> result =
+ callable.futureCall(SampleRowKeysRequest.create(TableId.of("my-table")));
+
+ Throwable expectedError =
+ new NotFoundException("fake error", null, GrpcStatusCode.of(Code.NOT_FOUND), false);
+ inner.response.setException(expectedError);
+
+ Throwable actualError = null;
+ try {
+ result.get(1, TimeUnit.SECONDS);
+ } catch (ExecutionException e) {
+ actualError = e.getCause();
+ }
+
+ assertThat(actualError).isEqualTo(expectedError);
+ }
+
+ static class FakeCallable
+ extends UnaryCallable<
+ com.google.bigtable.v2.SampleRowKeysRequest, List> {
+ com.google.bigtable.v2.SampleRowKeysRequest request;
+ ApiCallContext callContext;
+ SettableApiFuture> response = SettableApiFuture.create();
+
+ @Override
+ public ApiFuture> futureCall(
+ com.google.bigtable.v2.SampleRowKeysRequest request, ApiCallContext context) {
+ this.request = request;
+ this.callContext = context;
+
+ return response;
+ }
+ }
+}
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
index 527e41e046..5d16b623fd 100644
--- a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerCallableTest.java
@@ -32,7 +32,6 @@
import com.google.bigtable.v2.ReadModifyWriteRowResponse;
import com.google.bigtable.v2.ReadRowsRequest;
import com.google.bigtable.v2.ReadRowsResponse;
-import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.SampleRowKeysResponse;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
@@ -43,6 +42,8 @@
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TableId;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings;
import com.google.common.collect.ImmutableMap;
@@ -248,6 +249,24 @@ public void testGFELatencySampleRowKeys() throws InterruptedException {
assertThat(latency).isEqualTo(fakeServerTiming.get());
}
+ @Test
+ public void testGFELatencySampleRowKeysWithRequest() throws InterruptedException {
+ stub.sampleRowKeysCallableWithRequest().call(SampleRowKeysRequest.create(TableId.of(TABLE_ID)));
+
+ Thread.sleep(WAIT_FOR_METRICS_TIME_MS);
+ long latency =
+ StatsTestUtils.getAggregationValueAsLong(
+ localStats,
+ RpcViewConstants.BIGTABLE_GFE_LATENCY_VIEW,
+ ImmutableMap.of(
+ RpcMeasureConstants.BIGTABLE_OP, TagValue.create("Bigtable.SampleRowKeys"),
+ RpcMeasureConstants.BIGTABLE_STATUS, TagValue.create("OK")),
+ PROJECT_ID,
+ INSTANCE_ID,
+ APP_PROFILE_ID);
+ assertThat(latency).isEqualTo(fakeServerTiming.get());
+ }
+
@Test
public void testGFELatencyCheckAndMutateRow() throws InterruptedException {
ConditionalRowMutation mutation =
@@ -425,7 +444,8 @@ public void mutateRows(MutateRowsRequest request, StreamObserver observer) {
+ com.google.bigtable.v2.SampleRowKeysRequest request,
+ StreamObserver observer) {
observer.onNext(SampleRowKeysResponse.getDefaultInstance());
observer.onCompleted();
}
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 8f62060c97..06b923cad3 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
@@ -47,6 +47,7 @@
import com.google.bigtable.v2.ResponseParams;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
import com.google.cloud.bigtable.data.v2.models.Query;
import com.google.cloud.bigtable.data.v2.models.Row;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
@@ -105,7 +106,7 @@ public class BuiltinMetricsTracerTest {
private static final String INSTANCE_ID = "fake-instance";
private static final String APP_PROFILE_ID = "default";
private static final String TABLE_ID = "fake-table";
-
+ private static final String AUTHORIZED_VIEW_ID = "fake-authorized-view";
private static final String BAD_TABLE_ID = "non-exist-table";
private static final String ZONE = "us-west-1";
private static final String CLUSTER = "cluster-0";
@@ -272,6 +273,37 @@ public void testReadRowsOperationLatencies() {
assertThat(cluster.getAllValues()).containsExactly(CLUSTER);
}
+ @Test
+ public void testReadRowsOperationLatenciesOnAuthorizedView() {
+ when(mockFactory.newTracer(any(), any(), any()))
+ .thenAnswer(
+ (Answer)
+ invocationOnMock ->
+ new BuiltinMetricsTracer(
+ OperationType.ServerStreaming,
+ SpanName.of("Bigtable", "ReadRows"),
+ statsRecorderWrapper));
+ ArgumentCaptor operationLatency = ArgumentCaptor.forClass(Long.class);
+
+ Stopwatch stopwatch = Stopwatch.createStarted();
+ Lists.newArrayList(
+ stub.readRowsCallable()
+ .call(Query.create(AuthorizedViewId.of(TABLE_ID, AUTHORIZED_VIEW_ID)))
+ .iterator());
+ long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+
+ verify(statsRecorderWrapper).putOperationLatencies(operationLatency.capture());
+ // verify record operation is only called once
+ verify(statsRecorderWrapper)
+ .recordOperation(status.capture(), tableId.capture(), zone.capture(), cluster.capture());
+
+ assertThat(operationLatency.getValue()).isIn(Range.closed(SERVER_LATENCY, elapsed));
+ assertThat(status.getAllValues()).containsExactly("OK");
+ assertThat(tableId.getAllValues()).containsExactly(TABLE_ID);
+ assertThat(zone.getAllValues()).containsExactly(ZONE);
+ assertThat(cluster.getAllValues()).containsExactly(CLUSTER);
+ }
+
@Test
public void testGfeMetrics() {
when(mockFactory.newTracer(any(), any(), any()))
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 88a874b8c9..99b0ab5b5e 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
@@ -29,7 +29,6 @@
import com.google.bigtable.v2.ReadModifyWriteRowResponse;
import com.google.bigtable.v2.ReadRowsRequest;
import com.google.bigtable.v2.ReadRowsResponse;
-import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.SampleRowKeysResponse;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
@@ -40,6 +39,8 @@
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TableId;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStubSettings;
import com.google.common.collect.Queues;
@@ -139,6 +140,15 @@ public void testSampleRowKeysHeaders() throws Exception {
verifyHeaders(attemptCounts, startTimestamp);
}
+ @Test
+ public void testSampleRowKeysWithRequestHeaders() throws Exception {
+ long startTimestamp = System.currentTimeMillis() * 1000;
+ stub.sampleRowKeysCallableWithRequest()
+ .call(SampleRowKeysRequest.create(TableId.of(TABLE_ID)))
+ .get(0);
+ verifyHeaders(attemptCounts, startTimestamp);
+ }
+
@Test
public void testCheckAndMutateHeaders() throws Exception {
long startTimestamp = System.currentTimeMillis() * 1000;
@@ -233,7 +243,8 @@ public void mutateRows(MutateRowsRequest request, StreamObserver observer) {
+ com.google.bigtable.v2.SampleRowKeysRequest request,
+ StreamObserver observer) {
if (callCount.get() < attemptCounts - 1) {
callCount.incrementAndGet();
observer.onError(new StatusRuntimeException(Status.UNAVAILABLE));
diff --git a/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java
new file mode 100644
index 0000000000..83c40403f8
--- /dev/null
+++ b/google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/misc_utilities/AuthorizedViewTestHelper.java
@@ -0,0 +1,45 @@
+/*
+ * 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.misc_utilities;
+
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView;
+import com.google.cloud.bigtable.admin.v2.models.CreateAuthorizedViewRequest;
+import com.google.cloud.bigtable.admin.v2.models.FamilySubsets;
+import com.google.cloud.bigtable.admin.v2.models.SubsetView;
+import com.google.cloud.bigtable.test_helpers.env.TestEnvRule;
+import java.util.UUID;
+
+public class AuthorizedViewTestHelper {
+ public static String AUTHORIZED_VIEW_ROW_PREFIX = "row#";
+ public static String AUTHORIZED_VIEW_COLUMN_QUALIFIER = "qualifier";
+
+ public static AuthorizedView createTestAuthorizedView(TestEnvRule testEnvRule) {
+ String tableId = testEnvRule.env().getTableId();
+ String authorizedViewId = UUID.randomUUID().toString();
+ CreateAuthorizedViewRequest request =
+ CreateAuthorizedViewRequest.of(tableId, authorizedViewId)
+ .setAuthorizedViewType(
+ SubsetView.create()
+ .addRowPrefix(AUTHORIZED_VIEW_ROW_PREFIX)
+ .setFamilySubsets(
+ testEnvRule.env().getFamilyId(),
+ FamilySubsets.create()
+ .addQualifierPrefix(AUTHORIZED_VIEW_COLUMN_QUALIFIER)))
+ .setDeletionProtection(false);
+ return testEnvRule.env().getTableAdminClient().createAuthorizedView(request);
+ }
+}