Skip to content

Commit

Permalink
feat: Support AuthorizedView in bigtable data client
Browse files Browse the repository at this point in the history
Change-Id: I1e54cab5b384d76166183ac72105a4cbac59979b
  • Loading branch information
Lixia Chen committed Mar 20, 2024
1 parent d37860e commit 0d2c752
Show file tree
Hide file tree
Showing 36 changed files with 4,723 additions and 167 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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 +42,46 @@ 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);
}
}
Original file line number Diff line number Diff line change
@@ -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.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;

/**
* 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.
*/
public class AuthorizedViewId implements TargetId {
@Nonnull private final String tableId;
@Nonnull private final String authorizedViewId;

/** Constructs a new AuthorizedViewId object from the specified tableId and authorizedViewId. */
public static AuthorizedViewId of(@Nonnull String tableId, @Nonnull String authorizedViewId) {
return new AuthorizedViewId(tableId, authorizedViewId);
}

private AuthorizedViewId(@Nonnull String tableId, @Nonnull String authorizedViewId) {
Preconditions.checkNotNull(tableId, "table id can't be null.");
Preconditions.checkNotNull(authorizedViewId, "authorized view id can't be null.");
this.tableId = tableId;
this.authorizedViewId = authorizedViewId;
}

@Override
@InternalApi
public String toResourceName(String projectId, String instanceId) {
return NameUtil.formatAuthorizedViewName(projectId, instanceId, tableId, authorizedViewId);
}

@Override
public boolean scopedForAuthorizedView() {
return true;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AuthorizedViewId authorizedViewId = (AuthorizedViewId) o;
return Objects.equal(this.tableId, authorizedViewId.tableId)
&& Objects.equal(this.authorizedViewId, authorizedViewId.authorizedViewId);
}

@Override
public int hashCode() {
return Objects.hashCode(tableId, authorizedViewId);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("tableId", tableId)
.add("authorizedViewId", authorizedViewId)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@
*/
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;
Expand All @@ -48,10 +47,19 @@ public static BulkMutation create(String tableId) {
return new BulkMutation(tableId);
}

/** Creates a new instance of the bulk mutation builder for the given target with targetId. */
public static BulkMutation create(@Nonnull TargetId targetId) {
return new BulkMutation(targetId);
}

private BulkMutation(@Nonnull String tableId) {
Preconditions.checkNotNull(tableId);
this(TableId.of(tableId));
}

this.tableId = tableId;
private BulkMutation(@Nonnull TargetId targetId) {
Preconditions.checkNotNull(targetId, "target id can't be null.");

this.targetId = targetId;
this.builder = MutateRowsRequest.newBuilder();
}

Expand Down Expand Up @@ -117,14 +125,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 +149,27 @@ public MutateRowsRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
BulkMutation bulkMutation =
BulkMutation.create(NameUtil.extractTableIdFromTableName(request.getTableName()));
String tableName = request.getTableName();
String authorizedViewName = request.getAuthorizedViewName();

Preconditions.checkArgument(
!tableName.isEmpty() || !authorizedViewName.isEmpty(),
"Either table name or authorized view name must be specified");
Preconditions.checkArgument(
tableName.isEmpty() || authorizedViewName.isEmpty(),
"Table name and authorized view name cannot be specified at the same time");

BulkMutation bulkMutation;
if (!tableName.isEmpty()) {
bulkMutation =
BulkMutation.create(TableId.of(NameUtil.extractTableIdFromTableName(tableName)));
} else {
bulkMutation =
BulkMutation.create(
AuthorizedViewId.of(
NameUtil.extractTableIdFromAuthorizedViewName(authorizedViewName),
NameUtil.extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName)));
}
bulkMutation.builder = request.toBuilder();

return bulkMutation;
Expand All @@ -150,7 +178,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,12 +33,18 @@
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(@Nonnull String tableId, ByteString rowKey) {
this(TableId.of(tableId), rowKey);
}

private ConditionalRowMutation(@Nonnull TargetId targetId, ByteString rowKey) {
Preconditions.checkNotNull(targetId, "target id can't be null.");

this.targetId = targetId;
builder.setRowKey(rowKey);
}

Expand All @@ -47,13 +53,23 @@ public static ConditionalRowMutation create(String tableId, String rowKey) {
return create(tableId, ByteString.copyFromUtf8(rowKey));
}

/** Creates a new instance of the mutation builder for the given target with targetId. */
public static ConditionalRowMutation create(@Nonnull TargetId targetId, String rowKey) {
return create(targetId, ByteString.copyFromUtf8(rowKey));
}

/** Creates a new instance of the mutation builder. */
public static ConditionalRowMutation create(String tableId, ByteString rowKey) {
Validations.validateTableId(tableId);

return new ConditionalRowMutation(tableId, rowKey);
}

/** Creates a new instance of the mutation builder for the given target with targetId. */
public static ConditionalRowMutation create(@Nonnull TargetId targetId, ByteString rowKey) {
return new ConditionalRowMutation(targetId, rowKey);
}

private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
input.defaultReadObject();
builder = CheckAndMutateRowRequest.newBuilder().mergeFrom(input);
Expand All @@ -80,7 +96,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 +146,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 +166,28 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static ConditionalRowMutation fromProto(@Nonnull CheckAndMutateRowRequest request) {
String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
ConditionalRowMutation rowMutation =
ConditionalRowMutation.create(tableId, request.getRowKey());
String tableName = request.getTableName();
String authorizedViewName = request.getAuthorizedViewName();

Preconditions.checkArgument(
!tableName.isEmpty() || !authorizedViewName.isEmpty(),
"Either table name or authorized view name must be specified");
Preconditions.checkArgument(
tableName.isEmpty() || authorizedViewName.isEmpty(),
"Table name and authorized view name cannot be specified at the same time");

ConditionalRowMutation rowMutation;
if (!tableName.isEmpty()) {
String tableId = NameUtil.extractTableIdFromTableName(tableName);
rowMutation = ConditionalRowMutation.create(TableId.of(tableId), request.getRowKey());
} else {
String tableId = NameUtil.extractTableIdFromAuthorizedViewName(authorizedViewName);
String authorizedViewId =
NameUtil.extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName);
rowMutation =
ConditionalRowMutation.create(
AuthorizedViewId.of(tableId, authorizedViewId), request.getRowKey());
}
rowMutation.builder = request.toBuilder();

return rowMutation;
Expand Down

0 comments on commit 0d2c752

Please sign in to comment.