Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add admin APIs for AuthorizedView #2175

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Large diffs are not rendered by default.

Expand Up @@ -34,6 +34,9 @@ public class NameUtil {
private static final Pattern BACKUP_PATTERN =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/clusters/([^/]+)/backups/([^/]+)");

private static final Pattern AUTHORIZED_VIEW_PATTERN =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");

public static String formatProjectName(String projectId) {
return "projects/" + projectId;
}
Expand All @@ -55,6 +58,11 @@ public static String formatBackupName(
return formatClusterName(projectId, instanceId, clusterId) + "/backups/" + backupId;
}

public static String formatAuthorizedViewName(
String projectId, String instanceId, String tableId, String viewId) {
return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + viewId;
}

public static String extractTableIdFromTableName(String fullTableName) {
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
if (!matcher.matches()) {
Expand All @@ -71,6 +79,15 @@ public static String extractBackupIdFromBackupName(String fullBackupName) {
return matcher.group(4);
}

public static String extractAuthorizedViewIdFromAuthorizedViewName(
String fullAuthorizedViewName) {
Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
}
return matcher.group(4);
}

public static String extractZoneIdFromLocationName(String fullLocationName) {
Matcher matcher = LOCATION_PATTERN.matcher(fullLocationName);
if (!matcher.matches()) {
Expand Down
@@ -0,0 +1,123 @@
/*
* 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.admin.v2.models;

import com.google.api.core.InternalApi;
import com.google.bigtable.admin.v2.AuthorizedViewName;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;

/**
* A class that wraps the {@link com.google.bigtable.admin.v2.AuthorizedView} protocol buffer
* object.
*
* <p>An AuthorizedView represents subsets of a particular table based on rules. The access to each
* AuthorizedView can be configured separately from the Table.
*
* <p>Users can perform read/write operation on an AuthorizedView by providing an authorizedView id
* besides a table id, in which case the semantics remain identical as reading/writing on a Table
* except that visibility is restricted to the subset of the Table that the AuthorizedView
* represents.
*/
public final class AuthorizedView {
private final com.google.bigtable.admin.v2.AuthorizedView proto;

/**
* Wraps the protobuf. This method is considered an internal implementation detail and not meant
* to be used by applications.
*/
@InternalApi
public static AuthorizedView fromProto(
@Nonnull com.google.bigtable.admin.v2.AuthorizedView proto) {
return new AuthorizedView(proto);
}

private AuthorizedView(@Nonnull com.google.bigtable.admin.v2.AuthorizedView proto) {
Preconditions.checkNotNull(proto);
Preconditions.checkArgument(!proto.getName().isEmpty(), "AuthorizedView must have a name");
Preconditions.checkArgument(
proto.hasSubsetView(), "AuthorizedView must have a subset_view field");
trollyxia marked this conversation as resolved.
Show resolved Hide resolved
this.proto = proto;
}

/** Gets the authorized view's id. */
public String getId() {
// Constructor ensures that name is not null.
AuthorizedViewName fullName = AuthorizedViewName.parse(proto.getName());

//noinspection ConstantConditions
return fullName.getAuthorizedView();
}

/** Gets the id of the table that owns this authorized view. */
public String getTableId() {
// Constructor ensures that name is not null.
AuthorizedViewName fullName = AuthorizedViewName.parse(proto.getName());

//noinspection ConstantConditions
return fullName.getTable();
}

/** Returns whether this authorized view is deletion protected. */
public boolean isDeletionProtected() {
return proto.getDeletionProtection();
}

/** Gets the type of this authorized view, which currently can only be a subset view. */
public AuthorizedViewType getAuthorizedViewType() {
if (proto.hasSubsetView()) {
return SubsetView.fromProto(proto.getSubsetView());
} else {
// Should never happen because the constructor verifies that one must exist.
throw new IllegalStateException("This AuthorizedView doesn't have a valid type specified");
}
}

/**
* Creates the request protobuf. This method is considered an internal implementation detail and
* not meant to be used by applications.
*/
@InternalApi
public com.google.bigtable.admin.v2.AuthorizedView toProto() {
return proto;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AuthorizedView that = (AuthorizedView) o;
return Objects.equal(proto, that.proto);
}

@Override
public int hashCode() {
return Objects.hashCode(proto);
}

/**
* Represents a subset of a Table. Please check the implementations of this interface for more
* details.
*/
@InternalApi
public interface AuthorizedViewType {}
trollyxia marked this conversation as resolved.
Show resolved Hide resolved
}
@@ -0,0 +1,117 @@
/*
* 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.admin.v2.models;

import com.google.api.core.InternalApi;
import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
import com.google.cloud.bigtable.admin.v2.models.AuthorizedView.AuthorizedViewType;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;

/**
* Parameters for creating a new Cloud Bigtable {@link AuthorizedView}, which represents subsets of
* a particular table.
*
* <p>Sample code:
*
* <pre>{@code
* CreateAuthorizedViewRequest request =
* CreateAuthorizedViewRequest.of("my-table", "my-new-authorized-view")
* .setAuthorizedViewType(
* SubsetView.create()
* .addRowPrefix("row#")
* .addFamilySubsets(
* "my-family", FamilySubsets.create().addQualifier("column")));
* }</pre>
*
* @see AuthorizedView for more details.
*/
public final class CreateAuthorizedViewRequest {
private final String tableId;
private final com.google.bigtable.admin.v2.CreateAuthorizedViewRequest.Builder requestBuilder =
com.google.bigtable.admin.v2.CreateAuthorizedViewRequest.newBuilder();

public static CreateAuthorizedViewRequest of(
@Nonnull String tableId, @Nonnull String authorizedViewId) {
return new CreateAuthorizedViewRequest(tableId, authorizedViewId);
}

private CreateAuthorizedViewRequest(@Nonnull String tableId, @Nonnull String authorizedViewId) {
Preconditions.checkNotNull(tableId, "tableId must be set");
Preconditions.checkNotNull(authorizedViewId, "authorizedViewId must be set");

this.tableId = tableId;
requestBuilder.setAuthorizedViewId(authorizedViewId);
}

/** Configures if the authorized view is deletion protected. */
public CreateAuthorizedViewRequest setDeletionProtection(boolean deletionProtection) {
requestBuilder.getAuthorizedViewBuilder().setDeletionProtection(deletionProtection);
return this;
}

/**
* Sets the implementation for this authorized view.
*
* @see AuthorizedViewType for details.
*/
public CreateAuthorizedViewRequest setAuthorizedViewType(
@Nonnull AuthorizedViewType authorizedViewType) {
Preconditions.checkNotNull(authorizedViewType, "authorizedViewType must be set");

if (authorizedViewType instanceof SubsetView) {
requestBuilder
.getAuthorizedViewBuilder()
.setSubsetView(((SubsetView) authorizedViewType).toProto());
} else {
throw new IllegalArgumentException("Unknown authorizedViewType: " + authorizedViewType);
}

return this;
}

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

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

/**
* Creates the request protobuf. This method is considered an internal implementation detail and
* not meant to be used by applications.
*/
@InternalApi
public com.google.bigtable.admin.v2.CreateAuthorizedViewRequest toProto(
@Nonnull String projectId, @Nonnull String instanceId) {
return requestBuilder
.setParent(NameUtil.formatTableName(projectId, instanceId, tableId))
.build();
}
}
@@ -0,0 +1,118 @@
/*
* 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.admin.v2.models;

import com.google.api.core.InternalApi;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import javax.annotation.Nonnull;

/** Represents subsets of a particular column family that are included in this authorized view. */
public final class FamilySubsets {
private final com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets.Builder builder;

/**
* Wraps the protobuf. This method is considered an internal implementation detail and not meant
* to be used by applications.
*/
@InternalApi
public static FamilySubsets fromProto(
@Nonnull com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets proto) {
return new FamilySubsets(proto);
}

public static FamilySubsets create() {
return new FamilySubsets();
}

private FamilySubsets(@Nonnull com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets proto) {
this.builder = proto.toBuilder();
}

private FamilySubsets() {
this.builder = com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets.newBuilder();
}

/** Gets the list of column qualifiers included in this authorized view. */
public ImmutableList<ByteString> getQualifiers() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the API to return List cause we don't want to leak guava dependencies:

public List<ByteString> getQualifiers() {
   return ImmutableList.copyOf(...);
}

return ImmutableList.copyOf(this.builder.getQualifiersList());
}

/** Gets the list of column qualifier prefixes included in this authorized view. */
public ImmutableList<ByteString> getQualifierPrefixes() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

return ImmutableList.copyOf(this.builder.getQualifierPrefixesList());
}

/** Adds an individual column qualifier to be included in this authorized view. */
public FamilySubsets addQualifier(ByteString qualifier) {
this.builder.addQualifiers(qualifier);
return this;
}

/** Adds an individual column qualifier to be included in this authorized view. */
public FamilySubsets addQualifier(String qualifier) {
this.builder.addQualifiers(ByteString.copyFromUtf8(qualifier));
return this;
}

/**
* Adds a prefix for column qualifiers to be included in this authorized view. Every qualifier
* starting with the prefix will be included in this authorized view. An empty string ("") prefix
* means to provide access to all qualifiers.
*/
public FamilySubsets addQualifierPrefix(ByteString qualifierPrefix) {
this.builder.addQualifierPrefixes(qualifierPrefix);
return this;
}

/**
* Adds a prefix for column qualifiers to be included in this authorized view. Every qualifier
* starting with the prefix will be included in this authorized view. An empty string ("") prefix
* means to provide access to all qualifiers.
*/
public FamilySubsets addQualifierPrefix(String qualifierPrefix) {
this.builder.addQualifierPrefixes(ByteString.copyFromUtf8(qualifierPrefix));
return this;
}

/**
* Creates the request protobuf. This method is considered an internal implementation detail and
* not meant to be used by applications.
*/
@InternalApi
public com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets toProto() {
return builder.build();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FamilySubsets that = (FamilySubsets) o;
return Objects.equal(builder.build(), that.builder.build());
}

@Override
public int hashCode() {
return Objects.hashCode(builder.build());
}
}