Skip to content

Commit 0d2c752

Browse files
author
Lixia Chen
committed
feat: Support AuthorizedView in bigtable data client
Change-Id: I1e54cab5b384d76166183ac72105a4cbac59979b
1 parent d37860e commit 0d2c752

36 files changed

+4723
-167
lines changed

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

Lines changed: 776 additions & 9 deletions
Large diffs are not rendered by default.

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
public class NameUtil {
3131
private static final Pattern TABLE_PATTERN =
3232
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)");
33+
private static final Pattern AUTHORIZED_VIEW_PATTERN =
34+
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");
3335

3436
public static String formatInstanceName(@Nonnull String projectId, @Nonnull String instanceId) {
3537
return "projects/" + projectId + "/instances/" + instanceId;
@@ -40,11 +42,46 @@ public static String formatTableName(
4042
return formatInstanceName(projectId, instanceId) + "/tables/" + tableId;
4143
}
4244

45+
public static String formatAuthorizedViewName(
46+
@Nonnull String projectId,
47+
@Nonnull String instanceId,
48+
@Nonnull String tableId,
49+
@Nonnull String authorizedViewId) {
50+
return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + authorizedViewId;
51+
}
52+
4353
public static String extractTableIdFromTableName(@Nonnull String fullTableName) {
4454
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
4555
if (!matcher.matches()) {
4656
throw new IllegalArgumentException("Invalid table name: " + fullTableName);
4757
}
4858
return matcher.group(3);
4959
}
60+
61+
public static String extractTableIdFromAuthorizedViewName(
62+
@Nonnull String fullAuthorizedViewName) {
63+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
64+
if (!matcher.matches()) {
65+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
66+
}
67+
return matcher.group(3);
68+
}
69+
70+
public static String extractTableNameFromAuthorizedViewName(
71+
@Nonnull String fullAuthorizedViewName) {
72+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
73+
if (!matcher.matches()) {
74+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
75+
}
76+
return formatTableName(matcher.group(1), matcher.group(2), matcher.group(3));
77+
}
78+
79+
public static String extractAuthorizedViewIdFromAuthorizedViewName(
80+
@Nonnull String fullAuthorizedViewName) {
81+
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
82+
if (!matcher.matches()) {
83+
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
84+
}
85+
return matcher.group(4);
86+
}
5087
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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.cloud.bigtable.data.v2.internal.NameUtil;
21+
import com.google.common.base.MoreObjects;
22+
import com.google.common.base.Objects;
23+
import com.google.common.base.Preconditions;
24+
import javax.annotation.Nonnull;
25+
26+
/**
27+
* An implementation of a {@link TargetId} for authorized views.
28+
*
29+
* <p>See {@link com.google.cloud.bigtable.admin.v2.models.AuthorizedView} for more details about an
30+
* authorized view.
31+
*/
32+
public class AuthorizedViewId implements TargetId {
33+
@Nonnull private final String tableId;
34+
@Nonnull private final String authorizedViewId;
35+
36+
/** Constructs a new AuthorizedViewId object from the specified tableId and authorizedViewId. */
37+
public static AuthorizedViewId of(@Nonnull String tableId, @Nonnull String authorizedViewId) {
38+
return new AuthorizedViewId(tableId, authorizedViewId);
39+
}
40+
41+
private AuthorizedViewId(@Nonnull String tableId, @Nonnull String authorizedViewId) {
42+
Preconditions.checkNotNull(tableId, "table id can't be null.");
43+
Preconditions.checkNotNull(authorizedViewId, "authorized view id can't be null.");
44+
this.tableId = tableId;
45+
this.authorizedViewId = authorizedViewId;
46+
}
47+
48+
@Override
49+
@InternalApi
50+
public String toResourceName(String projectId, String instanceId) {
51+
return NameUtil.formatAuthorizedViewName(projectId, instanceId, tableId, authorizedViewId);
52+
}
53+
54+
@Override
55+
public boolean scopedForAuthorizedView() {
56+
return true;
57+
}
58+
59+
@Override
60+
public boolean equals(Object o) {
61+
if (this == o) {
62+
return true;
63+
}
64+
if (o == null || getClass() != o.getClass()) {
65+
return false;
66+
}
67+
AuthorizedViewId authorizedViewId = (AuthorizedViewId) o;
68+
return Objects.equal(this.tableId, authorizedViewId.tableId)
69+
&& Objects.equal(this.authorizedViewId, authorizedViewId.authorizedViewId);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return Objects.hashCode(tableId, authorizedViewId);
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return MoreObjects.toStringHelper(this)
80+
.add("tableId", tableId)
81+
.add("authorizedViewId", authorizedViewId)
82+
.toString();
83+
}
84+
}

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

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
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;
@@ -48,10 +47,19 @@ public static BulkMutation create(String tableId) {
4847
return new BulkMutation(tableId);
4948
}
5049

50+
/** Creates a new instance of the bulk mutation builder for the given target with targetId. */
51+
public static BulkMutation create(@Nonnull TargetId targetId) {
52+
return new BulkMutation(targetId);
53+
}
54+
5155
private BulkMutation(@Nonnull String tableId) {
52-
Preconditions.checkNotNull(tableId);
56+
this(TableId.of(tableId));
57+
}
5358

54-
this.tableId = tableId;
59+
private BulkMutation(@Nonnull TargetId targetId) {
60+
Preconditions.checkNotNull(targetId, "target id can't be null.");
61+
62+
this.targetId = targetId;
5563
this.builder = MutateRowsRequest.newBuilder();
5664
}
5765

@@ -117,14 +125,15 @@ public int getEntryCount() {
117125

118126
@InternalApi
119127
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();
128+
String resourceName =
129+
targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
130+
if (targetId.scopedForAuthorizedView()) {
131+
builder.setAuthorizedViewName(resourceName);
132+
} else {
133+
builder.setTableName(resourceName);
134+
}
135+
136+
return builder.setAppProfileId(requestContext.getAppProfileId()).build();
128137
}
129138

130139
/**
@@ -140,8 +149,27 @@ public MutateRowsRequest toProto(RequestContext requestContext) {
140149
*/
141150
@BetaApi
142151
public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
143-
BulkMutation bulkMutation =
144-
BulkMutation.create(NameUtil.extractTableIdFromTableName(request.getTableName()));
152+
String tableName = request.getTableName();
153+
String authorizedViewName = request.getAuthorizedViewName();
154+
155+
Preconditions.checkArgument(
156+
!tableName.isEmpty() || !authorizedViewName.isEmpty(),
157+
"Either table name or authorized view name must be specified");
158+
Preconditions.checkArgument(
159+
tableName.isEmpty() || authorizedViewName.isEmpty(),
160+
"Table name and authorized view name cannot be specified at the same time");
161+
162+
BulkMutation bulkMutation;
163+
if (!tableName.isEmpty()) {
164+
bulkMutation =
165+
BulkMutation.create(TableId.of(NameUtil.extractTableIdFromTableName(tableName)));
166+
} else {
167+
bulkMutation =
168+
BulkMutation.create(
169+
AuthorizedViewId.of(
170+
NameUtil.extractTableIdFromAuthorizedViewName(authorizedViewName),
171+
NameUtil.extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName)));
172+
}
145173
bulkMutation.builder = request.toBuilder();
146174

147175
return bulkMutation;
@@ -150,7 +178,7 @@ public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
150178
/** Creates a copy of {@link BulkMutation}. */
151179
@Override
152180
public BulkMutation clone() {
153-
BulkMutation bulkMutation = BulkMutation.create(tableId);
181+
BulkMutation bulkMutation = BulkMutation.create(targetId);
154182
bulkMutation.builder = this.builder.clone();
155183
return bulkMutation;
156184
}

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

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@
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(@Nonnull String tableId, ByteString rowKey) {
41+
this(TableId.of(tableId), rowKey);
42+
}
43+
44+
private ConditionalRowMutation(@Nonnull TargetId targetId, ByteString rowKey) {
45+
Preconditions.checkNotNull(targetId, "target id can't be null.");
46+
47+
this.targetId = targetId;
4248
builder.setRowKey(rowKey);
4349
}
4450

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

56+
/** Creates a new instance of the mutation builder for the given target with targetId. */
57+
public static ConditionalRowMutation create(@Nonnull TargetId targetId, String rowKey) {
58+
return create(targetId, ByteString.copyFromUtf8(rowKey));
59+
}
60+
5061
/** Creates a new instance of the mutation builder. */
5162
public static ConditionalRowMutation create(String tableId, ByteString rowKey) {
5263
Validations.validateTableId(tableId);
5364

5465
return new ConditionalRowMutation(tableId, rowKey);
5566
}
5667

68+
/** Creates a new instance of the mutation builder for the given target with targetId. */
69+
public static ConditionalRowMutation create(@Nonnull TargetId targetId, ByteString rowKey) {
70+
return new ConditionalRowMutation(targetId, rowKey);
71+
}
72+
5773
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
5874
input.defaultReadObject();
5975
builder = CheckAndMutateRowRequest.newBuilder().mergeFrom(input);
@@ -80,7 +96,8 @@ public ConditionalRowMutation condition(@Nonnull Filter condition) {
8096
Preconditions.checkNotNull(condition);
8197
Preconditions.checkState(
8298
!builder.hasPredicateFilter(),
83-
"Can only have a single condition, please use a Filters#chain or Filters#interleave filter instead");
99+
"Can only have a single condition, please use a Filters#chain or Filters#interleave filter"
100+
+ " instead");
84101
// TODO: verify that the condition does not use any FILTERS.condition() filters
85102

86103
builder.setPredicateFilter(condition.toProto());
@@ -129,13 +146,16 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
129146
Preconditions.checkState(
130147
!builder.getTrueMutationsList().isEmpty() || !builder.getFalseMutationsList().isEmpty(),
131148
"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();
149+
150+
String resourceName =
151+
targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
152+
if (targetId.scopedForAuthorizedView()) {
153+
builder.setAuthorizedViewName(resourceName);
154+
} else {
155+
builder.setTableName(resourceName);
156+
}
157+
158+
return builder.setAppProfileId(requestContext.getAppProfileId()).build();
139159
}
140160

141161
/**
@@ -146,9 +166,28 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
146166
*/
147167
@BetaApi
148168
public static ConditionalRowMutation fromProto(@Nonnull CheckAndMutateRowRequest request) {
149-
String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
150-
ConditionalRowMutation rowMutation =
151-
ConditionalRowMutation.create(tableId, request.getRowKey());
169+
String tableName = request.getTableName();
170+
String authorizedViewName = request.getAuthorizedViewName();
171+
172+
Preconditions.checkArgument(
173+
!tableName.isEmpty() || !authorizedViewName.isEmpty(),
174+
"Either table name or authorized view name must be specified");
175+
Preconditions.checkArgument(
176+
tableName.isEmpty() || authorizedViewName.isEmpty(),
177+
"Table name and authorized view name cannot be specified at the same time");
178+
179+
ConditionalRowMutation rowMutation;
180+
if (!tableName.isEmpty()) {
181+
String tableId = NameUtil.extractTableIdFromTableName(tableName);
182+
rowMutation = ConditionalRowMutation.create(TableId.of(tableId), request.getRowKey());
183+
} else {
184+
String tableId = NameUtil.extractTableIdFromAuthorizedViewName(authorizedViewName);
185+
String authorizedViewId =
186+
NameUtil.extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName);
187+
rowMutation =
188+
ConditionalRowMutation.create(
189+
AuthorizedViewId.of(tableId, authorizedViewId), request.getRowKey());
190+
}
152191
rowMutation.builder = request.toBuilder();
153192

154193
return rowMutation;

0 commit comments

Comments
 (0)