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.

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);
}
}
}
@@ -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;
}
}
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
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.