Skip to content

Commit 4b255d0

Browse files
trollyxiaLixia Chen
and
Lixia Chen
authored
feat: Support AuthorizedView in bigtable data client (#2177)
* 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>
1 parent 13d1df3 commit 4b255d0

37 files changed

+4581
-229
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java

+822-9
Large diffs are not rendered by default.

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java

+68
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package com.google.cloud.bigtable.data.v2.internal;
1717

1818
import com.google.api.core.InternalApi;
19+
import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
20+
import com.google.cloud.bigtable.data.v2.models.TableId;
21+
import com.google.cloud.bigtable.data.v2.models.TargetId;
1922
import java.util.regex.Matcher;
2023
import java.util.regex.Pattern;
2124
import javax.annotation.Nonnull;
@@ -30,6 +33,8 @@
3033
public class NameUtil {
3134
private static final Pattern TABLE_PATTERN =
3235
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)");
36+
private static final Pattern AUTHORIZED_VIEW_PATTERN =
37+
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");
3338

3439
public static String formatInstanceName(@Nonnull String projectId, @Nonnull String instanceId) {
3540
return "projects/" + projectId + "/instances/" + instanceId;
@@ -40,11 +45,74 @@ public static String formatTableName(
4045
return formatInstanceName(projectId, instanceId) + "/tables/" + tableId;
4146
}
4247

48+
public static String formatAuthorizedViewName(
49+
@Nonnull String projectId,
50+
@Nonnull String instanceId,
51+
@Nonnull String tableId,
52+
@Nonnull String authorizedViewId) {
53+
return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + authorizedViewId;
54+
}
55+
4356
public static String extractTableIdFromTableName(@Nonnull String fullTableName) {
4457
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
4558
if (!matcher.matches()) {
4659
throw new IllegalArgumentException("Invalid table name: " + fullTableName);
4760
}
4861
return matcher.group(3);
4962
}
63+
64+
public static String extractTableIdFromAuthorizedViewName(
65+
@Nonnull String fullAuthorizedViewName) {
66+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
67+
if (!matcher.matches()) {
68+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
69+
}
70+
return matcher.group(3);
71+
}
72+
73+
public static String extractTableNameFromAuthorizedViewName(
74+
@Nonnull String fullAuthorizedViewName) {
75+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
76+
if (!matcher.matches()) {
77+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
78+
}
79+
return formatTableName(matcher.group(1), matcher.group(2), matcher.group(3));
80+
}
81+
82+
public static String extractAuthorizedViewIdFromAuthorizedViewName(
83+
@Nonnull String fullAuthorizedViewName) {
84+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
85+
if (!matcher.matches()) {
86+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
87+
}
88+
return matcher.group(4);
89+
}
90+
91+
/** A helper to convert fully qualified tableName and authorizedViewName to a {@link TargetId} */
92+
public static TargetId extractTargetId(
93+
@Nonnull String tableName, @Nonnull String authorizedViewName) {
94+
if (tableName.isEmpty() && authorizedViewName.isEmpty()) {
95+
throw new IllegalArgumentException(
96+
"Either table name or authorized view name must be specified. Table name: "
97+
+ tableName
98+
+ ", authorized view name: "
99+
+ authorizedViewName);
100+
}
101+
if (!tableName.isEmpty() && !authorizedViewName.isEmpty()) {
102+
throw new IllegalArgumentException(
103+
"Table name and authorized view name cannot be specified at the same time. Table name: "
104+
+ tableName
105+
+ ", authorized view name: "
106+
+ authorizedViewName);
107+
}
108+
109+
if (!tableName.isEmpty()) {
110+
String tableId = extractTableIdFromTableName(tableName);
111+
return TableId.of(tableId);
112+
} else {
113+
String tableId = extractTableIdFromAuthorizedViewName(authorizedViewName);
114+
String authorizedViewId = extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName);
115+
return AuthorizedViewId.of(tableId, authorizedViewId);
116+
}
117+
}
50118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigtable.data.v2.models;
18+
19+
import com.google.api.core.InternalApi;
20+
import com.google.auto.value.AutoValue;
21+
import com.google.cloud.bigtable.data.v2.internal.NameUtil;
22+
import com.google.common.base.Preconditions;
23+
24+
/**
25+
* An implementation of a {@link TargetId} for authorized views.
26+
*
27+
* <p>See {@link com.google.cloud.bigtable.admin.v2.models.AuthorizedView} for more details about an
28+
* authorized view.
29+
*/
30+
@AutoValue
31+
public abstract class AuthorizedViewId implements TargetId {
32+
/** Constructs a new AuthorizedViewId object from the specified tableId and authorizedViewId. */
33+
public static AuthorizedViewId of(String tableId, String authorizedViewId) {
34+
Preconditions.checkNotNull(tableId, "table id can't be null.");
35+
Preconditions.checkNotNull(authorizedViewId, "authorized view id can't be null.");
36+
return new AutoValue_AuthorizedViewId(tableId, authorizedViewId);
37+
}
38+
39+
abstract String getTableId();
40+
41+
abstract String getAuthorizedViewId();
42+
43+
@Override
44+
@InternalApi
45+
public String toResourceName(String projectId, String instanceId) {
46+
return NameUtil.formatAuthorizedViewName(
47+
projectId, instanceId, getTableId(), getAuthorizedViewId());
48+
}
49+
50+
@Override
51+
@InternalApi
52+
public boolean scopedForAuthorizedView() {
53+
return true;
54+
}
55+
}

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java

+31-16
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,31 @@
3838
*/
3939
public final class BulkMutation implements Serializable, Cloneable {
4040
private static final long serialVersionUID = 3522061250439399088L;
41-
42-
private final String tableId;
41+
private final TargetId targetId;
4342
private transient MutateRowsRequest.Builder builder;
4443

4544
private long mutationCountSum = 0;
4645

46+
/** @deprecated Please use {@link BulkMutation#create(TargetId)} instead. */
47+
@Deprecated
4748
public static BulkMutation create(String tableId) {
48-
return new BulkMutation(tableId);
49+
return new BulkMutation(TableId.of(tableId));
4950
}
5051

51-
private BulkMutation(@Nonnull String tableId) {
52-
Preconditions.checkNotNull(tableId);
52+
/**
53+
* Creates a new instance of the bulk mutation builder for the given target with targetId.
54+
*
55+
* @see AuthorizedViewId
56+
* @see TableId
57+
*/
58+
public static BulkMutation create(TargetId targetId) {
59+
return new BulkMutation(targetId);
60+
}
61+
62+
private BulkMutation(TargetId targetId) {
63+
Preconditions.checkNotNull(targetId, "target id can't be null.");
5364

54-
this.tableId = tableId;
65+
this.targetId = targetId;
5566
this.builder = MutateRowsRequest.newBuilder();
5667
}
5768

@@ -117,14 +128,15 @@ public int getEntryCount() {
117128

118129
@InternalApi
119130
public MutateRowsRequest toProto(RequestContext requestContext) {
120-
String tableName =
121-
NameUtil.formatTableName(
122-
requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
123-
124-
return builder
125-
.setTableName(tableName)
126-
.setAppProfileId(requestContext.getAppProfileId())
127-
.build();
131+
String resourceName =
132+
targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
133+
if (targetId.scopedForAuthorizedView()) {
134+
builder.setAuthorizedViewName(resourceName);
135+
} else {
136+
builder.setTableName(resourceName);
137+
}
138+
139+
return builder.setAppProfileId(requestContext.getAppProfileId()).build();
128140
}
129141

130142
/**
@@ -140,8 +152,11 @@ public MutateRowsRequest toProto(RequestContext requestContext) {
140152
*/
141153
@BetaApi
142154
public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
155+
String tableName = request.getTableName();
156+
String authorizedViewName = request.getAuthorizedViewName();
157+
143158
BulkMutation bulkMutation =
144-
BulkMutation.create(NameUtil.extractTableIdFromTableName(request.getTableName()));
159+
BulkMutation.create(NameUtil.extractTargetId(tableName, authorizedViewName));
145160
bulkMutation.builder = request.toBuilder();
146161

147162
return bulkMutation;
@@ -150,7 +165,7 @@ public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
150165
/** Creates a copy of {@link BulkMutation}. */
151166
@Override
152167
public BulkMutation clone() {
153-
BulkMutation bulkMutation = BulkMutation.create(tableId);
168+
BulkMutation bulkMutation = BulkMutation.create(targetId);
154169
bulkMutation.builder = this.builder.clone();
155170
return bulkMutation;
156171
}

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java

+47-16
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,49 @@
3333
public final class ConditionalRowMutation implements Serializable {
3434
private static final long serialVersionUID = -3699904745621909502L;
3535

36-
private final String tableId;
36+
private final TargetId targetId;
3737
private transient CheckAndMutateRowRequest.Builder builder =
3838
CheckAndMutateRowRequest.newBuilder();
3939

40-
private ConditionalRowMutation(String tableId, ByteString rowKey) {
41-
this.tableId = tableId;
40+
private ConditionalRowMutation(TargetId targetId, ByteString rowKey) {
41+
Preconditions.checkNotNull(targetId, "target id can't be null.");
42+
43+
this.targetId = targetId;
4244
builder.setRowKey(rowKey);
4345
}
4446

45-
/** Creates a new instance of the mutation builder. */
47+
/** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, String)} instead. */
48+
@Deprecated
4649
public static ConditionalRowMutation create(String tableId, String rowKey) {
4750
return create(tableId, ByteString.copyFromUtf8(rowKey));
4851
}
4952

50-
/** Creates a new instance of the mutation builder. */
53+
/**
54+
* Creates a new instance of the mutation builder for the given target with targetId.
55+
*
56+
* @see AuthorizedViewId
57+
* @see TableId
58+
*/
59+
public static ConditionalRowMutation create(TargetId targetId, String rowKey) {
60+
return create(targetId, ByteString.copyFromUtf8(rowKey));
61+
}
62+
63+
/** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, ByteString)} instead. */
64+
@Deprecated
5165
public static ConditionalRowMutation create(String tableId, ByteString rowKey) {
5266
Validations.validateTableId(tableId);
5367

54-
return new ConditionalRowMutation(tableId, rowKey);
68+
return new ConditionalRowMutation(TableId.of(tableId), rowKey);
69+
}
70+
71+
/**
72+
* Creates a new instance of the mutation builder for the given target with targetId.
73+
*
74+
* @see AuthorizedViewId
75+
* @see TableId
76+
*/
77+
public static ConditionalRowMutation create(TargetId targetId, ByteString rowKey) {
78+
return new ConditionalRowMutation(targetId, rowKey);
5579
}
5680

5781
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -80,7 +104,8 @@ public ConditionalRowMutation condition(@Nonnull Filter condition) {
80104
Preconditions.checkNotNull(condition);
81105
Preconditions.checkState(
82106
!builder.hasPredicateFilter(),
83-
"Can only have a single condition, please use a Filters#chain or Filters#interleave filter instead");
107+
"Can only have a single condition, please use a Filters#chain or Filters#interleave filter"
108+
+ " instead");
84109
// TODO: verify that the condition does not use any FILTERS.condition() filters
85110

86111
builder.setPredicateFilter(condition.toProto());
@@ -129,13 +154,16 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
129154
Preconditions.checkState(
130155
!builder.getTrueMutationsList().isEmpty() || !builder.getFalseMutationsList().isEmpty(),
131156
"ConditionalRowMutations must have `then` or `otherwise` mutations.");
132-
String tableName =
133-
NameUtil.formatTableName(
134-
requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
135-
return builder
136-
.setTableName(tableName.toString())
137-
.setAppProfileId(requestContext.getAppProfileId())
138-
.build();
157+
158+
String resourceName =
159+
targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
160+
if (targetId.scopedForAuthorizedView()) {
161+
builder.setAuthorizedViewName(resourceName);
162+
} else {
163+
builder.setTableName(resourceName);
164+
}
165+
166+
return builder.setAppProfileId(requestContext.getAppProfileId()).build();
139167
}
140168

141169
/**
@@ -146,9 +174,12 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
146174
*/
147175
@BetaApi
148176
public static ConditionalRowMutation fromProto(@Nonnull CheckAndMutateRowRequest request) {
149-
String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
177+
String tableName = request.getTableName();
178+
String authorizedViewName = request.getAuthorizedViewName();
179+
150180
ConditionalRowMutation rowMutation =
151-
ConditionalRowMutation.create(tableId, request.getRowKey());
181+
ConditionalRowMutation.create(
182+
NameUtil.extractTargetId(tableName, authorizedViewName), request.getRowKey());
152183
rowMutation.builder = request.toBuilder();
153184

154185
return rowMutation;

0 commit comments

Comments
 (0)