-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(bigquery): add samples for control access 1/3 #4023
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
Changes from 1 commit
f2b62df
0041270
54f2250
47b16e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // Copyright 2025 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 | ||
| // | ||
| // http://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. | ||
|
|
||
| 'use strict'; | ||
|
|
||
| /** | ||
| * Grants access to a BigQuery dataset for a specified entity. | ||
| * | ||
| * @param {string} datasetId ID of the dataset to grant access to. | ||
| * @param {string} entityId ID of the entity to grant access to. | ||
| * @param {string} role Role to grant. | ||
| * @returns {Promise<Array>} Array of access entries. | ||
| */ | ||
| async function grantAccessToDataset(datasetId, entityId, role) { | ||
| // [START bigquery_grant_access_to_dataset] | ||
| const {BigQuery} = require('@google-cloud/bigquery'); | ||
|
|
||
| // Define enum for HTTP codes. | ||
| const HTTP_STATUS = { | ||
| PRECONDITION_FAILED: 412, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: can we use a library here instead of defining our own constant? Maybe something from the gRPC library for Node? |
||
| }; | ||
|
|
||
| // TODO(developer): Update and un-comment below lines. | ||
|
telpirion marked this conversation as resolved.
Outdated
|
||
|
|
||
| // ID of the dataset to revoke access to. | ||
| // datasetId = "my_project_id.my_dataset_name"; | ||
|
|
||
| // ID of the user or group from whom you are adding access. | ||
| // Alternatively, the JSON REST API representation of the entity, | ||
| // such as a view's table reference. | ||
| // entityId = "user-or-group-to-add@example.com"; | ||
|
|
||
| // One of the "Basic roles for datasets" described here: | ||
| // https://cloud.google.com/bigquery/docs/access-control-basic-roles#dataset-basic-roles | ||
| // role = "READER"; | ||
|
|
||
| // Type of entity you are granting access to. | ||
| // Find allowed allowed entity type names here: | ||
| // https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets#resource:-dataset | ||
| // In this case, we're using groupByEmail | ||
| const entityType = 'groupByEmail'; | ||
|
|
||
| // Instantiate a client. | ||
| const client = new BigQuery(); | ||
|
|
||
| try { | ||
| // Get a reference to the dataset. | ||
| const [dataset] = await client.dataset(datasetId).get(); | ||
|
|
||
| // The 'access entries' array is immutable. Create a copy for modifications. | ||
| const entries = Array.isArray(dataset.metadata.access) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: don't use ternary conditions in samples, as they are harder to read. The condition that we're testing is "is |
||
| ? [...dataset.metadata.access] | ||
| : []; | ||
|
|
||
| // Append an AccessEntry to grant the role to a dataset. | ||
| // Find more details about the AccessEntry object in the BigQuery documentation: | ||
| // https://cloud.google.com/python/docs/reference/bigquery/latest/google.cloud.bigquery.dataset.AccessEntry | ||
| entries.push({ | ||
| role: role, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: you can shorten this operation to just entries.push({
role,
[entityType]: entityId,
}) |
||
| [entityType]: entityId, | ||
| }); | ||
|
|
||
| // Assign the array of AccessEntries back to the dataset. | ||
| const metadata = { | ||
| access: entries, | ||
| }; | ||
|
|
||
| // Update will only succeed if the dataset | ||
| // has not been modified externally since retrieval. | ||
| // | ||
| // See the BigQuery client library documentation for more details on metadata updates: | ||
| // https://cloud.google.com/nodejs/docs/reference/bigquery/latest | ||
|
|
||
| // Update just the 'access entries' property of the dataset. | ||
| const [updatedDataset] = await client | ||
| .dataset(datasetId) | ||
| .setMetadata(metadata); | ||
|
|
||
| // Show a success message. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: cut this comment, as it doesn't add any significant new information to what the code does. |
||
| console.log( | ||
| `Role '${role}' granted for entity '${entityId}' in dataset '${datasetId}'.` | ||
| ); | ||
|
|
||
| return updatedDataset.access; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: process results from the method call in the sample. See: https://googlecloudplatform.github.io/samples-style-guide/#result |
||
| } catch (error) { | ||
| if (error.code === HTTP_STATUS.PRECONDITION_FAILED) { | ||
| console.error( | ||
| `Dataset '${datasetId}' was modified remotely before this update. ` + | ||
| 'Fetch the latest version and retry.' | ||
| ); | ||
|
telpirion marked this conversation as resolved.
Outdated
|
||
| } else { | ||
| throw error; | ||
| } | ||
| } | ||
| // [END bigquery_grant_access_to_dataset] | ||
| } | ||
|
|
||
| module.exports = { | ||
| grantAccessToDataset, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // Copyright 2025 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 | ||
| // | ||
| // http://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. | ||
|
|
||
| 'use strict'; | ||
|
|
||
| /** | ||
| * Grants access to a BigQuery table or view for a specified principal. | ||
| * | ||
| * @param {string} projectId Google Cloud Platform project ID. | ||
| * @param {string} datasetId Dataset where the table or view is. | ||
| * @param {string} resourceName Table or view name to get the access policy. | ||
| * @param {string} principalId The principal requesting access to the table or view. | ||
| * @param {string} role Role to assign to the member. | ||
| * @returns {Promise<object[]>} The updated policy bindings. | ||
| */ | ||
| async function grantAccessToTableOrView( | ||
| projectId, | ||
| datasetId, | ||
| resourceName, | ||
| principalId, | ||
| role | ||
| ) { | ||
| // [START bigquery_grant_access_to_table_or_view] | ||
| const {BigQuery} = require('@google-cloud/bigquery'); | ||
|
|
||
| // TODO(developer): Update and un-comment below lines. | ||
|
telpirion marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Google Cloud Platform project. | ||
| // projectId = "my_project_id" | ||
|
|
||
| // Dataset where the table or view is. | ||
| // datasetId = "my_dataset_id" | ||
|
|
||
| // Table or view name to get the access policy. | ||
| // resourceName = "my_table_id" | ||
|
|
||
| // The principal requesting access to the table or view. | ||
| // Find more details about principal identifiers here: | ||
| // https://cloud.google.com/iam/docs/principal-identifiers | ||
| // principalId = "user:bob@example.com" | ||
|
|
||
| // Role to assign to the member. | ||
| // role = "roles/bigquery.dataViewer" | ||
|
|
||
| // Instantiate a client. | ||
| const client = new BigQuery(); | ||
|
|
||
| // Get a reference to the dataset by datasetId. | ||
| const dataset = client.dataset(datasetId); | ||
| // Get a reference to the table by tableName. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: insert space between getting the dataset and the comment about getting the table. In fact, I think you can remove both comments, as the intention of the code is clear. See https://googlecloudplatform.github.io/samples-style-guide/#comments |
||
| const table = dataset.table(resourceName); | ||
|
|
||
| // Get the IAM access policy for the table or view. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: include a link to the IAM policies for tables, for example https://cloud.google.com/bigquery/docs/access-control. |
||
| const [policy] = await table.getIamPolicy(); | ||
|
|
||
| // Initialize bindings array. | ||
| if (!policy.bindings) { | ||
| policy.bindings = []; | ||
| } | ||
|
|
||
| // To grant access to a table or view | ||
| // add bindings to the Table or View policy. | ||
| // | ||
| // Find more details about Policy and Binding objects here: | ||
| // https://cloud.google.com/security-command-center/docs/reference/rest/Shared.Types/Policy | ||
| // https://cloud.google.com/security-command-center/docs/reference/rest/Shared.Types/Binding | ||
| const binding = { | ||
| role: role, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: as above, you can use shorthand property name here. |
||
| members: [principalId], | ||
| }; | ||
| policy.bindings.push(binding); | ||
|
|
||
| // Set the IAM access policy with updated bindings. | ||
| const [updatedPolicy] = await table.setIamPolicy(policy); | ||
|
|
||
| // Show a success message. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: remove comment. |
||
| console.log( | ||
| `Role '${role}' granted for principal '${principalId}' on resource '${datasetId}.${resourceName}'.` | ||
| ); | ||
| // [END bigquery_grant_access_to_table_or_view] | ||
| return updatedPolicy.bindings; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: same as above -- samples shouldn't return a result. |
||
| } | ||
|
|
||
| module.exports = {grantAccessToTableOrView}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| { | ||
| "name": "bigquery-cloud-client", | ||
| "description": "Big Query Cloud Client Node.js samples", | ||
| "version": "0.0.1", | ||
| "private": true, | ||
| "license": "Apache Version 2.0", | ||
| "author": "Google LLC", | ||
| "engines": { | ||
| "node": "20.x" | ||
| }, | ||
| "scripts": { | ||
| "deploy": "gcloud app deploy", | ||
| "start": "node app.js", | ||
| "unit-test": "c8 mocha -p -j 2 test/ --timeout=10000 --exit", | ||
| "test": "npm run unit-test" | ||
| }, | ||
| "dependencies": { | ||
| "@google-cloud/bigquery": "7.9.2" | ||
| }, | ||
| "devDependencies": { | ||
| "c8": "^10.0.0", | ||
| "chai": "^4.5.0", | ||
| "mocha": "^10.0.0", | ||
| "sinon": "^18.0.0" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we typically write the Node samples so that there is an inner function and an outer function, where the outer function exposes arguments that are passed through to the inner function.