Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions bigquery/cloud-client/grantAccessToDataset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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';

async function main(datasetId, entityId, role) {
// [START bigquery_grant_access_to_dataset]

/**
* TODO(developer): Update and un-comment below lines.
*/

// const datasetId = "my_project_id.my_dataset_name";

// ID of the user or group from whom you are adding access.
// const 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
// const role = "READER";

const {BigQuery} = require('@google-cloud/bigquery');

// Instantiate a client.
const client = new BigQuery();

// 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
const entityType = 'groupByEmail';

async function grantAccessToDataset() {
const [dataset] = await client.dataset(datasetId).get();

// The 'access entries' array is immutable. Create a copy for modifications.
const entries = [...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,
[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.
await client.dataset(datasetId).setMetadata(metadata);

console.log(
`Role '${role}' granted for entity '${entityId}' in '${datasetId}'.`
);
}
// [END bigquery_grant_access_to_dataset]
await grantAccessToDataset();
}

exports.grantAccessToDataset = main;
71 changes: 71 additions & 0 deletions bigquery/cloud-client/grantAccessToTableOrView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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';

async function main(projectId, datasetId, tableId, principalId, role) {
// [START bigquery_grant_access_to_table_or_view]

/**
* TODO(developer): Update and un-comment below lines
*/
// const projectId = "YOUR_PROJECT_ID";
// const datasetId = "YOUR_DATASET_ID";
// const tableId = "YOUR_TABLE_ID";
// const principalId = "YOUR_PRINCIPAL_ID";
// const role = "YOUR_ROLE";

const {BigQuery} = require('@google-cloud/bigquery');

// Instantiate a client.
const client = new BigQuery();

async function grantAccessToTableOrView() {
const dataset = client.dataset(datasetId);
const table = dataset.table(tableId);

// Get the IAM access policy for the table or view.
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,
members: [principalId],
};
policy.bindings.push(binding);

// Set the IAM access policy with updated bindings.
await table.setIamPolicy(policy);

// Show a success message.
console.log(
`Role '${role}' granted for principal '${principalId}' on resource '${datasetId}.${tableId}'.`
);
}

await grantAccessToTableOrView();
// [END bigquery_grant_access_to_table_or_view]
}

exports.grantAccessToTableOrView = main;
26 changes: 26 additions & 0 deletions bigquery/cloud-client/package.json
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"
}
}
52 changes: 52 additions & 0 deletions bigquery/cloud-client/test/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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.

const uuid = require('uuid');
const {BigQuery} = require('@google-cloud/bigquery');

// Setup and teardown functions for test suites
const setupBeforeAll = async () => {
const prefix = `nodejs_test_${uuid.v4().replace(/-/g, '').substring(0, 8)}`;
const entityId = 'example-analyst-group@google.com'; // Group account
const datasetId = `${prefix}_cloud_client`;
const tableName = `${prefix}_table`;
const viewName = `${prefix}_view`;

const client = new BigQuery();
await client
.createDataset(datasetId)
.then(() => {
return client.dataset(datasetId).createTable(tableName);
})
.catch(err => {
console.error(`Error creating table: ${err.message}`);
});

return {
datasetId: datasetId,
tableId: tableName,
viewId: viewName,
entityId: entityId,
};
};

const cleanupResources = async datasetId => {
const client = new BigQuery();
await client.dataset(datasetId).delete({deleteContents: true, force: true});
};

module.exports = {
setupBeforeAll,
cleanupResources,
};
58 changes: 58 additions & 0 deletions bigquery/cloud-client/test/grantAccessToDataset.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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';

const {beforeEach, afterEach, it, describe} = require('mocha');
const assert = require('assert');
const sinon = require('sinon');

const {setupBeforeAll, cleanupResources} = require('./config');

const {grantAccessToDataset} = require('../grantAccessToDataset');

describe('grantAccessToDataset', () => {
let datasetId = null;
let entityId = null;
const role = 'READER';

beforeEach(async () => {
const response = await setupBeforeAll();
datasetId = response.datasetId;
entityId = response.entityId;

sinon.stub(console, 'log');
sinon.stub(console, 'error');
});

// Clean up after all tests.
afterEach(async () => {
await cleanupResources(datasetId);
console.log.restore();
console.error.restore();
});

it('should add entity to access entries', async () => {
// Act: Grant access to the dataset.
await grantAccessToDataset(datasetId, entityId, role);

// Check if our entity ID is in the updated access entries.
assert.strictEqual(
console.log.calledWith(
`Role '${role}' granted for entity '${entityId}' in '${datasetId}'.`
),
true
);
});
});
65 changes: 65 additions & 0 deletions bigquery/cloud-client/test/grantAccessToTableOrView.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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';

const {describe, it, beforeEach, afterEach} = require('mocha');
const assert = require('assert');
const sinon = require('sinon');

const {grantAccessToTableOrView} = require('../grantAccessToTableOrView');
const {setupBeforeAll, cleanupResources} = require('./config');

describe('grantAccessToTableOrView', () => {
let datasetId = null;
let entityId = null;
let tableId = null;
const projectId = process.env.GCLOUD_PROJECT;

beforeEach(async () => {
const response = await setupBeforeAll();
datasetId = response.datasetId;
entityId = response.entityId;
tableId = response.tableId;

sinon.stub(console, 'log');
sinon.stub(console, 'error');
});

afterEach(async () => {
await cleanupResources(datasetId);
console.log.restore();
console.error.restore();
});

it('should grant access to a table', async () => {
const roleId = 'roles/bigquery.dataViewer';
const principalId = `group:${entityId}`;

await grantAccessToTableOrView(
projectId,
datasetId,
tableId,
principalId,
roleId
);

assert.strictEqual(
console.log.calledWith(
`Role '${roleId}' granted for principal '${principalId}' on resource '${datasetId}.${tableId}'.`
),
true
);
});
});