diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java index 48ff86342..115e001bd 100644 --- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java +++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/Acl.java @@ -20,9 +20,11 @@ import com.google.api.core.ApiFunction; import com.google.api.services.bigquery.model.Dataset.Access; +import com.google.api.services.bigquery.model.DatasetAccessEntry; import com.google.cloud.StringEnumType; import com.google.cloud.StringEnumValue; import java.io.Serializable; +import java.util.List; import java.util.Objects; /** @@ -105,7 +107,8 @@ public enum Type { USER, VIEW, IAM_MEMBER, - ROUTINE + ROUTINE, + DATASET } Entity(Type type) { @@ -119,6 +122,11 @@ public Type getType() { abstract Access toPb(); static Entity fromPb(Access access) { + if (access.getDataset() != null) { + return new DatasetAclEntity( + DatasetId.fromPb(access.getDataset().getDataset()), + access.getDataset().getTargetTypes()); + } if (access.getDomain() != null) { return new Domain(access.getDomain()); } @@ -146,6 +154,65 @@ static Entity fromPb(Access access) { } } + /** + * Class for a BigQuery DatasetAclEntity ACL entity. Objects of this class represent a + * DatasetAclEntity from a different DatasetAclEntity to grant access to. Only views are supported + * for now. The role field is not required when this field is set. If that DatasetAclEntity is + * deleted and re-created, its access needs to be granted again via an update operation. + */ + public static final class DatasetAclEntity extends Entity { + + private static final long serialVersionUID = -8392885851733136526L; + + private final DatasetId id; + private final List targetTypes; + + /** Creates a DatasetAclEntity given the DatasetAclEntity's id. */ + public DatasetAclEntity(DatasetId id, List targetTypes) { + super(Type.DATASET); + this.id = id; + this.targetTypes = targetTypes; + } + + /** Returns DatasetAclEntity's identity. */ + public DatasetId getId() { + return id; + } + + public List getTargetTypes() { + return targetTypes; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + DatasetAclEntity datasetAclEntity = (DatasetAclEntity) obj; + return Objects.equals(getType(), datasetAclEntity.getType()) + && Objects.equals(id, datasetAclEntity.id); + } + + @Override + public int hashCode() { + return Objects.hash(getType(), id); + } + + @Override + public String toString() { + return toPb().toString(); + } + + @Override + Access toPb() { + return new Access() + .setDataset(new DatasetAccessEntry().setDataset(id.toPb()).setTargetTypes(targetTypes)); + } + } + /** * Class for a BigQuery Domain entity. Objects of this class represent a domain to grant access * to. Any users signed in with the domain specified will be granted the specified access. @@ -342,9 +409,10 @@ Access toPb() { /** * Class for a BigQuery View entity. Objects of this class represent a view from a different - * dataset to grant access to. Queries executed against that view will have read access to tables - * in this dataset. The role field is not required when this field is set. If that view is updated - * by any user, access to the view needs to be granted again via an update operation. + * datasetAclEntity to grant access to. Queries executed against that view will have read access + * to tables in this datasetAclEntity. The role field is not required when this field is set. If + * that view is updated by any user, access to the view needs to be granted again via an update + * operation. */ public static final class View extends Entity { @@ -393,10 +461,10 @@ Access toPb() { /** * Class for a BigQuery Routine entity. Objects of this class represent a routine from a different - * dataset to grant access to. Queries executed against that routine will have read access to - * views/tables/routines in this dataset. Only UDF is supported for now. The role field is not - * required when this field is set. If that routine is updated by any user, access to the routine - * needs to be granted again via an update operation. + * datasetAclEntity to grant access to. Queries executed against that routine will have read + * access to views/tables/routines in this datasetAclEntity. Only UDF is supported for now. The + * role field is not required when this field is set. If that routine is updated by any user, + * access to the routine needs to be granted again via an update operation. */ public static final class Routine extends Entity { @@ -516,6 +584,11 @@ public static Acl of(Entity entity, Role role) { return new Acl(entity, role); } + /** Returns an Acl object for a datasetAclEntity. */ + public static Acl of(DatasetAclEntity datasetAclEntity) { + return new Acl(datasetAclEntity, null); + } + /** Returns an Acl object for a view entity. */ public static Acl of(View view) { return new Acl(view, null); diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java index 736803391..30866c2b6 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/AclTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; import com.google.api.services.bigquery.model.Dataset; +import com.google.cloud.bigquery.Acl.DatasetAclEntity; import com.google.cloud.bigquery.Acl.Domain; import com.google.cloud.bigquery.Acl.Entity; import com.google.cloud.bigquery.Acl.Entity.Type; @@ -27,10 +28,23 @@ import com.google.cloud.bigquery.Acl.Role; import com.google.cloud.bigquery.Acl.User; import com.google.cloud.bigquery.Acl.View; +import com.google.common.collect.ImmutableList; +import java.util.List; import org.junit.Test; public class AclTest { + @Test + public void testDatasetEntity() { + DatasetId datasetId = DatasetId.of("dataset"); + List targetTypes = ImmutableList.of("VIEWS"); + DatasetAclEntity entity = new DatasetAclEntity(datasetId, targetTypes); + assertEquals(datasetId, entity.getId()); + assertEquals(targetTypes, entity.getTargetTypes()); + Dataset.Access pb = entity.toPb(); + assertEquals(entity, Entity.fromPb(pb)); + } + @Test public void testDomainEntity() { Domain entity = new Domain("d1"); diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java index 30bb0db0b..0b0cd0207 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/SerializationTest.java @@ -20,6 +20,7 @@ import com.google.cloud.NoCredentials; import com.google.cloud.PageImpl; import com.google.cloud.Restorable; +import com.google.cloud.bigquery.Acl.DatasetAclEntity; import com.google.cloud.bigquery.StandardTableDefinition.StreamingBuffer; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -38,8 +39,6 @@ public class SerializationTest extends BaseSerializationTest { Acl.of(new Acl.View(TableId.of("project", "dataset", "table")), Acl.Role.WRITER); private static final Acl ROUTINE_ACCESS = Acl.of(new Acl.Routine(RoutineId.of("project", "dataset", "routine")), Acl.Role.WRITER); - private static final List ACCESS_RULES = - ImmutableList.of(DOMAIN_ACCESS, GROUP_ACCESS, VIEW_ACCESS, ROUTINE_ACCESS, USER_ACCESS); private static final Long CREATION_TIME = System.currentTimeMillis() - 10; private static final Long DEFAULT_TABLE_EXPIRATION = 100L; private static final String DESCRIPTION = "Description"; @@ -50,6 +49,11 @@ public class SerializationTest extends BaseSerializationTest { private static final String LOCATION = ""; private static final String SELF_LINK = "http://bigquery/p/d"; private static final DatasetId DATASET_ID = DatasetId.of("project", "dataset"); + private static final List TARGET_TYPES = ImmutableList.of("VIEWS"); + private static final Acl DATASET_ACCESS = Acl.of(new DatasetAclEntity(DATASET_ID, TARGET_TYPES)); + private static final List ACCESS_RULES = + ImmutableList.of( + DOMAIN_ACCESS, GROUP_ACCESS, VIEW_ACCESS, ROUTINE_ACCESS, USER_ACCESS, DATASET_ACCESS); private static final DatasetInfo DATASET_INFO = DatasetInfo.newBuilder(DATASET_ID) .setAcl(ACCESS_RULES) @@ -228,6 +232,7 @@ protected Serializable[] serializableObjects() { USER_ACCESS, VIEW_ACCESS, ROUTINE_ACCESS, + DATASET_ACCESS, DATASET_ID, DATASET_INFO, TABLE_ID, diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 09c9b7a7b..b8051122f 100644 --- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -38,6 +38,7 @@ import com.google.cloud.Role; import com.google.cloud.ServiceOptions; import com.google.cloud.bigquery.Acl; +import com.google.cloud.bigquery.Acl.DatasetAclEntity; import com.google.cloud.bigquery.BigQuery; import com.google.cloud.bigquery.BigQuery.DatasetDeleteOption; import com.google.cloud.bigquery.BigQuery.DatasetField; @@ -1897,6 +1898,47 @@ public void testAuthorizeRoutine() { assertEquals(routineAcl, routineDataset.getAcl()); } + @Test + public void testAuthorizeDataset() { + String datasetName = RemoteBigQueryHelper.generateDatasetName(); + DatasetId datasetId = DatasetId.of(PROJECT_ID, datasetName); + List targetTypes = ImmutableList.of("VIEWS"); + // Specify the acl which will be shared to the authorized dataset + List acl = + ImmutableList.of( + Acl.of(new Acl.Group("projectOwners"), Acl.Role.OWNER), + Acl.of(new Acl.IamMember("allUsers"), Acl.Role.READER)); + DatasetInfo datasetInfo = + DatasetInfo.newBuilder(datasetId).setAcl(acl).setDescription("shared Dataset").build(); + Dataset sharedDataset = bigquery.create(datasetInfo); + assertNotNull(sharedDataset); + assertEquals(sharedDataset.getDescription(), "shared Dataset"); + // Get the current metadata for the dataset you want to share by calling the datasets.get method + List sharedDatasetAcl = new ArrayList<>(sharedDataset.getAcl()); + + // Create a new dataset to be authorized + String authorizedDatasetName = RemoteBigQueryHelper.generateDatasetName(); + DatasetId authorizedDatasetId = DatasetId.of(PROJECT_ID, authorizedDatasetName); + DatasetInfo authorizedDatasetInfo = + DatasetInfo.newBuilder(authorizedDatasetId) + .setDescription("new Dataset to be authorized by the sharedDataset") + .build(); + Dataset authorizedDataset = bigquery.create(authorizedDatasetInfo); + assertNotNull(authorizedDataset); + assertEquals( + authorizedDataset.getDescription(), "new Dataset to be authorized by the sharedDataset"); + + // Add the new DatasetAccessEntry object to the existing sharedDatasetAcl list + DatasetAclEntity datasetEntity = new DatasetAclEntity(authorizedDatasetId, targetTypes); + sharedDatasetAcl.add(Acl.of(datasetEntity)); + + // Update the dataset with the added authorization + Dataset updatedDataset = sharedDataset.toBuilder().setAcl(sharedDatasetAcl).build().update(); + + // Verify that the authorized dataset has been added + assertEquals(sharedDatasetAcl, updatedDataset.getAcl()); + } + @Test public void testSingleStatementsQueryException() throws InterruptedException { String invalidQuery =