Skip to content

Commit

Permalink
feat: Support AuthorizedView in bigtable data client (#2177)
Browse files Browse the repository at this point in the history
* feat: Support AuthorizedView in bigtable data client

Change-Id: I1e54cab5b384d76166183ac72105a4cbac59979b

chore: Address review comments

Change-Id: I1d9c8bc54d204dbe6752161291cbd4158ee1d6b5

chore: Add @deprecated annotation and reformat javadoc

Change-Id: I3c4a0fcbecc124a9517e48a1fc747c7b8dade40e

chore: Remove ReadRow(s)Options

Change-Id: Ib35bdac2cd51f302fd919671324fb2c0d630af0e

chore: Minor doc and test fix

Change-Id: I3c8e1fa624ec0423433eae15059b5e59e9727c70

Chore: Address comments

Change-Id: I8886e907ebb797a67b36240417c2e609b6f5857a

Rename SampleRowKeys to SampleRowKeysRequest

Change-Id: I8dda7ee1df31b184d04938cbc2f9f984d84138b4

Add javadoc for extractTargetId and hide scopedForAuthorizedView

Change-Id: I38718ae9badf24db6edc1b62fc06e4d3222faeb8

fix extractTableId to correctly handle authorized view name

Change-Id: I1e66fc1440a29d3861e3ee6464911324633cc5af

* Fix extractTableId() and add a unit test for it

Change-Id: Icc172b2b6d369ef8ce2b77a8e69c37af6e9aa3d7

* Mark SampleRowKeysRequest#fromProto as @internalapi

Change-Id: I71762fad534fe31f4bf634ca5cb227f014393e37

---------

Co-authored-by: Lixia Chen <lixiachen@google.com>
  • Loading branch information
trollyxia and Lixia Chen committed Mar 27, 2024
1 parent 13d1df3 commit 4b255d0
Show file tree
Hide file tree
Showing 37 changed files with 4,581 additions and 229 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -40,11 +45,74 @@ 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()) {
throw new IllegalArgumentException("Invalid table name: " + 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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();
}

/**
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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();
}

/**
Expand All @@ -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;
Expand Down

0 comments on commit 4b255d0

Please sign in to comment.