From 9156e62778cc35b0aa394eecbca7e482f13620e8 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Thu, 11 Sep 2025 06:49:15 +0000 Subject: [PATCH 1/5] feat: add storage batch operations samples --- storagebatchoperations/cancelJob.js | 69 ++++++++++++++++++++++ storagebatchoperations/createJob.js | 87 ++++++++++++++++++++++++++++ storagebatchoperations/deleteJob.js | 64 ++++++++++++++++++++ storagebatchoperations/getJob.js | 64 ++++++++++++++++++++ storagebatchoperations/listJobs.js | 63 ++++++++++++++++++++ storagebatchoperations/package.json | 23 ++++++++ storagebatchoperations/quickstart.js | 63 ++++++++++++++++++++ 7 files changed, 433 insertions(+) create mode 100644 storagebatchoperations/cancelJob.js create mode 100644 storagebatchoperations/createJob.js create mode 100644 storagebatchoperations/deleteJob.js create mode 100644 storagebatchoperations/getJob.js create mode 100644 storagebatchoperations/listJobs.js create mode 100644 storagebatchoperations/package.json create mode 100644 storagebatchoperations/quickstart.js diff --git a/storagebatchoperations/cancelJob.js b/storagebatchoperations/cancelJob.js new file mode 100644 index 0000000000..39fa4d8b02 --- /dev/null +++ b/storagebatchoperations/cancelJob.js @@ -0,0 +1,69 @@ +// 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 +// +// 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. + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +function main(projectId, jobId) { + // [START storage_batch_cancel_job] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // A unique identifier for this job. + // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; + + // Imports the Control library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + // Instantiates a client + const client = new StorageBatchOperationsClient(); + + async function cancelJob() { + const name = client.jobPath(projectId, 'global', jobId); + + // Create the request + const request = { + name, + }; + + // Run request + try { + await client.cancelJob(request); + console.log(`Cancelled job: ${name}`); + } catch (err) { + // This might be expected if the job completed quickly or failed creation + console.log(`INFO: cancelJob threw: ${err.message}`); + } + } + + cancelJob().catch(console.error); + // [END storage_batch_cancel_job] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/storagebatchoperations/createJob.js b/storagebatchoperations/createJob.js new file mode 100644 index 0000000000..1c29cda013 --- /dev/null +++ b/storagebatchoperations/createJob.js @@ -0,0 +1,87 @@ +// 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 +// +// 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. + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +function main(projectId, jobId, bucketName, objectPrefix) { + // [START storage_batch_create_job] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // The name of your GCS bucket + // const bucketName = 'bucketName'; + + // A unique identifier for this job. + // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; + + // The prefix of objects to include in the operation. + // const objectPrefix = 'prefix1'; + + // Imports the Control library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + // Instantiates a client + const client = new StorageBatchOperationsClient(); + + async function createJob() { + const parent = await client.locationPath(projectId, 'global'); + + // Create the request + const request = { + parent, + jobId, + job: { + bucketList: { + buckets: [ + { + bucket: bucketName, + prefixList: { + includedObjectPrefixes: [objectPrefix], + }, + }, + ], + }, + deleteObject: { + permanentObjectDeletionEnabled: false, + }, + }, + }; + + // Run request + const [operation] = await client.createJob(request); + const [response] = await operation.promise(); + console.log(`Created job: ${response.name}`); + } + + createJob(); + // [END storage_batch_create_job] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/storagebatchoperations/deleteJob.js b/storagebatchoperations/deleteJob.js new file mode 100644 index 0000000000..b452b2634b --- /dev/null +++ b/storagebatchoperations/deleteJob.js @@ -0,0 +1,64 @@ +// 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 +// +// 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. + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +function main(projectId, jobId) { + // [START storage_batch_delete_job] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // A unique identifier for this job. + // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; + + // Imports the Control library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + // Instantiates a client + const client = new StorageBatchOperationsClient(); + + async function deleteJob() { + const name = client.jobPath(projectId, 'global', jobId); + + // Create the request + const request = { + name, + }; + + // Run request + await client.deleteJob(request); + console.log(`Deleted job: ${name}`); + } + + deleteJob(); + // [END storage_batch_delete_job] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/storagebatchoperations/getJob.js b/storagebatchoperations/getJob.js new file mode 100644 index 0000000000..dbe4948c98 --- /dev/null +++ b/storagebatchoperations/getJob.js @@ -0,0 +1,64 @@ +// 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 +// +// 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. + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +function main(projectId, jobId) { + // [START storage_batch_get_job] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // A unique identifier for this job. + // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; + + // Imports the Control library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + // Instantiates a client + const client = new StorageBatchOperationsClient(); + + async function getJob() { + const name = client.jobPath(projectId, 'global', jobId); + + // Create the request + const request = { + name, + }; + + // Run request + const [response] = await client.getJob(request); + console.log(`Got job: ${response.name}`); + } + + getJob(); + // [END storage_batch_get_job] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/storagebatchoperations/listJobs.js b/storagebatchoperations/listJobs.js new file mode 100644 index 0000000000..4e79d1b822 --- /dev/null +++ b/storagebatchoperations/listJobs.js @@ -0,0 +1,63 @@ +// 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 +// +// 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. + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +function main(projectId) { + // [START storage_batch_list_jobs] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // Imports the Control library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + // Instantiates a client + const client = new StorageBatchOperationsClient(); + + async function listJobs() { + const parent = await client.locationPath(projectId, 'global'); + + // Create the request + const request = { + parent, + }; + + // Run request + const [response] = await client.listJobs(request); + for (const jobs of response) { + console.log(jobs.name); + } + } + + listJobs(); + // [END storage_batch_list_jobs] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/storagebatchoperations/package.json b/storagebatchoperations/package.json new file mode 100644 index 0000000000..867abcb1d3 --- /dev/null +++ b/storagebatchoperations/package.json @@ -0,0 +1,23 @@ +{ + "name": "storage-batch-operations-samples", + "version": "0.0.1", + "author": "Google Inc.", + "license": "Apache-2.0", + "description": "Examples of how to utilize the @google-cloud/storagebatchoperations library.", + "scripts": { + "test": "c8 mocha -p -j 2 system-test --timeout 600000" + }, + "repository": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" + }, + "devDependencies": { + "@google-cloud/storage": "^7.17.1", + "@google-cloud/storagebatchoperations": "^0.1.0", + "c8": "^10.0.0", + "chai": "^4.5.0", + "mocha": "^10.7.0", + "uuid": "^10.0.0" + } + } + \ No newline at end of file diff --git a/storagebatchoperations/quickstart.js b/storagebatchoperations/quickstart.js new file mode 100644 index 0000000000..9d5e9302e3 --- /dev/null +++ b/storagebatchoperations/quickstart.js @@ -0,0 +1,63 @@ +// 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. +// + +'use strict'; + +/** + * This application demonstrates how to perform basic operations on an Batch Operations + * instance with the Google Cloud Storage API. + * + * For more information, see the documentation at https://cloud.google.com/storage/docs/batch-operations/overview. + */ + +async function main(projectId, jobId) { + // [START storage_batch_quickstart] + + // Imports the Google Cloud client library + const {StorageBatchOperationsClient} = + require('@google-cloud/storagebatchoperations').v1; + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // Your Google Cloud project ID. + // const projectId = 'my-project-id'; + + // A unique identifier for this job. + // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; + + // Creates a client + const client = new StorageBatchOperationsClient(); + + async function quickstart() { + const name = client.jobPath(projectId, 'global', jobId); + + // Create the request + const request = { + name, + }; + + // Run request + const [response] = await client.getJob(request); + console.log(`Got job: ${response.name}`); + } + quickstart(); + // [END storage_batch_quickstart] +} + +main(...process.argv.slice(2)); + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); From 9f9360cf546ac484a047aae8871e80060883f8fd Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Thu, 11 Sep 2025 06:57:38 +0000 Subject: [PATCH 2/5] bug fix --- storagebatchoperations/quickstart.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storagebatchoperations/quickstart.js b/storagebatchoperations/quickstart.js index 9d5e9302e3..7fd6e42a0d 100644 --- a/storagebatchoperations/quickstart.js +++ b/storagebatchoperations/quickstart.js @@ -1,3 +1,5 @@ +// 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 @@ -9,7 +11,6 @@ // 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'; From d943f7fe63b393e91a3a85e78381641f016348cb Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Sep 2025 07:16:57 +0000 Subject: [PATCH 3/5] added test case --- storagebatchoperations/listJobs.js | 4 +- .../storagebatchoperations.test.js | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 storagebatchoperations/system-test/storagebatchoperations.test.js diff --git a/storagebatchoperations/listJobs.js b/storagebatchoperations/listJobs.js index 4e79d1b822..fd87266585 100644 --- a/storagebatchoperations/listJobs.js +++ b/storagebatchoperations/listJobs.js @@ -47,8 +47,8 @@ function main(projectId) { // Run request const [response] = await client.listJobs(request); - for (const jobs of response) { - console.log(jobs.name); + for (const job of response) { + console.log(job.name); } } diff --git a/storagebatchoperations/system-test/storagebatchoperations.test.js b/storagebatchoperations/system-test/storagebatchoperations.test.js new file mode 100644 index 0000000000..42113a0286 --- /dev/null +++ b/storagebatchoperations/system-test/storagebatchoperations.test.js @@ -0,0 +1,87 @@ +// 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 +// +// 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. + +const {Storage, Bucket} = require('@google-cloud/storage'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const uuid = require('uuid'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const projectId = process.env.GCLOUD_PROJECT; +const bucketPrefix = 'sbo-samples'; +const bucketName = `${bucketPrefix}-${uuid.v4()}`; +const storage = new Storage(); +const bucket = new Bucket(storage, bucketName); +const jobId = uuid.v4(); +const jobName = `projects/${projectId}/locations/global/jobs/${jobId}`; + +describe('Batch Operations', () => { + before(async () => { + await storage.createBucket(bucketName, { + iamConfiguration: { + uniformBucketLevelAccess: { + enabled: true, + }, + }, + hierarchicalNamespace: {enabled: true}, + }); + }); + + after(async () => { + await bucket.delete(); + }); + + it('should create a job', async () => { + const output = execSync( + `node createJob.js ${projectId} ${jobId} ${bucketName} objectPrefix` + ); + assert.match(output, /Created job:/); + assert.match(output, new RegExp(jobName)); + }); + + it('should list jobs', async () => { + const output = execSync(`node listJobs.js ${projectId}`); + assert.match(output, new RegExp(jobName)); + }); + + it('should run quickstart', async () => { + const output = execSync(`node quickstart.js ${projectId} ${jobId}`); + assert.match(output, /Got job:/); + assert.match(output, new RegExp(jobName)); + }); + + it('should get a job', async () => { + const output = execSync(`node getJob.js ${projectId} ${jobId}`); + assert.match(output, /Got job:/); + assert.match(output, new RegExp(jobName)); + }); + + it('should cancel a job', async () => { + try { + const output = execSync(`node cancelJob.js ${projectId} ${jobId}`); + assert.match(output, /Cancelled job:/); + assert.match(output, new RegExp(jobName)); + } catch (error) { + // This might be expected if the job completed quickly or failed creation + assert.match(error.message, /INFO: cancelJob threw: /); + } + }); + + it('should delete a job', async () => { + const output = execSync(`node deleteJob.js ${projectId} ${jobId}`); + assert.match(output, /Deleted job:/); + assert.match(output, new RegExp(jobName)); + }); +}); From df2e34c2f31930f057ff2742cee7929c9e904a10 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 12 Sep 2025 07:28:54 +0000 Subject: [PATCH 4/5] lint fix --- storagebatchoperations/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/storagebatchoperations/package.json b/storagebatchoperations/package.json index 867abcb1d3..e382054dda 100644 --- a/storagebatchoperations/package.json +++ b/storagebatchoperations/package.json @@ -20,4 +20,3 @@ "uuid": "^10.0.0" } } - \ No newline at end of file From 6f5d87051154193fa4517901b0428ee27ca14175 Mon Sep 17 00:00:00 2001 From: Thiyagu K Date: Fri, 17 Oct 2025 12:27:40 +0000 Subject: [PATCH 5/5] feat(storage-batch): Improve Batch Operation API samples (Documentation & Error Handling) Improve the robustness and clarity of the Storage Batch Operations API samples: * **Error Handling:** Wraps all asynchronous Batch Operations API samples (Create, Get, List, Cancel) in `try...catch` blocks for production readiness. * **Specific Diagnostics:** Adds specific gRPC error code checks (`NOT_FOUND`, `FAILED_PRECONDITION`) within the `catch` blocks to provide detailed diagnostic feedback to users regarding job state or non-existence. * **Documentation:** Clarifies JSDoc for all function parameters (e.g., `projectId`, `jobId`, `objectPrefix`), ensuring examples and types are clear for developers. --- storagebatchoperations/cancelJob.js | 40 ++++++++++++----- storagebatchoperations/createJob.js | 40 +++++++++-------- storagebatchoperations/deleteJob.js | 37 +++++++++++----- storagebatchoperations/getJob.js | 43 ++++++++++++++----- storagebatchoperations/listJobs.js | 36 ++++++++++++---- storagebatchoperations/quickstart.js | 42 ++++++++++++++---- .../storagebatchoperations.test.js | 22 +++++++--- 7 files changed, 191 insertions(+), 69 deletions(-) diff --git a/storagebatchoperations/cancelJob.js b/storagebatchoperations/cancelJob.js index 39fa4d8b02..7e739fc076 100644 --- a/storagebatchoperations/cancelJob.js +++ b/storagebatchoperations/cancelJob.js @@ -23,16 +23,19 @@ function main(projectId, jobId) { // [START storage_batch_cancel_job] + /** - * TODO(developer): Uncomment these variables before running the sample. + * Cancel a batch job instance. + * + * The operation to cancel a batch job instance in Google Cloud Storage (GCS) is used to stop + * a running or queued asynchronous task that is currently processing a large number of GCS objects. + * + * @param {string} projectId The Google Cloud project ID. + * Example: 'my-project-id' + * @param {string} jobId A unique identifier for this job. + * Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - - // A unique identifier for this job. - // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; - // Imports the Control library const {StorageBatchOperationsClient} = require('@google-cloud/storagebatchoperations').v1; @@ -52,13 +55,30 @@ function main(projectId, jobId) { try { await client.cancelJob(request); console.log(`Cancelled job: ${name}`); - } catch (err) { + } catch (error) { // This might be expected if the job completed quickly or failed creation - console.log(`INFO: cancelJob threw: ${err.message}`); + console.error( + `Error canceling batch jobs for jobId ${jobId}:`, + error.message + ); + + if (error.code === 5) { + // NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist. + console.error( + `Ensure the job '${jobId}' exists in project '${projectId}'.` + ); + } else if (error.code === 9) { + // FAILED_PRECONDITION (gRPC code 9) can occur if the job is already being cancelled + // or is not in a RUNNING state that allows the cancel operation. + console.error( + `Batch job '${jobId}' may not be in a state that allows canceling (e.g., must be RUNNING).` + ); + } + throw error; } } - cancelJob().catch(console.error); + cancelJob(); // [END storage_batch_cancel_job] } diff --git a/storagebatchoperations/createJob.js b/storagebatchoperations/createJob.js index 1c29cda013..0f6eba4134 100644 --- a/storagebatchoperations/createJob.js +++ b/storagebatchoperations/createJob.js @@ -23,22 +23,20 @@ function main(projectId, jobId, bucketName, objectPrefix) { // [START storage_batch_create_job] + /** - * TODO(developer): Uncomment these variables before running the sample. + * Create a new batch job instance. + * + * @param {string} projectId Your Google Cloud project ID. + * Example: 'my-project-id' + * @param {string} bucketName The name of your GCS bucket. + * Example: 'your-gcp-bucket-name' + * @param {string} jobId A unique identifier for this job. + * Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5' + * @param {string} objectPrefix The prefix of objects to include in the operation. + * Example: 'prefix1' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - - // The name of your GCS bucket - // const bucketName = 'bucketName'; - - // A unique identifier for this job. - // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; - - // The prefix of objects to include in the operation. - // const objectPrefix = 'prefix1'; - // Imports the Control library const {StorageBatchOperationsClient} = require('@google-cloud/storagebatchoperations').v1; @@ -70,10 +68,18 @@ function main(projectId, jobId, bucketName, objectPrefix) { }, }; - // Run request - const [operation] = await client.createJob(request); - const [response] = await operation.promise(); - console.log(`Created job: ${response.name}`); + try { + // Run the request, which returns an Operation object + const [operation] = await client.createJob(request); + console.log(`Waiting for operation ${operation.name} to complete...`); + + // Wait for the operation to complete and get the final resource + const [response] = await operation.promise(); + console.log(`Created job: ${response.name}`); + } catch (error) { + console.error('Failed to create batch job:', error.message); + throw error; + } } createJob(); diff --git a/storagebatchoperations/deleteJob.js b/storagebatchoperations/deleteJob.js index b452b2634b..0934a2311f 100644 --- a/storagebatchoperations/deleteJob.js +++ b/storagebatchoperations/deleteJob.js @@ -24,15 +24,17 @@ function main(projectId, jobId) { // [START storage_batch_delete_job] /** - * TODO(developer): Uncomment these variables before running the sample. + * Delete a batch job instance. + * + * This operation is used to remove a completed, failed, or cancelled Batch Operation + * job from the system's list. It is essentially a cleanup action. + * + * @param {string} projectId Your Google Cloud project ID. + * Example: 'my-project-id' + * @param {string} jobId A unique identifier for this job. + * Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - - // A unique identifier for this job. - // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; - // Imports the Control library const {StorageBatchOperationsClient} = require('@google-cloud/storagebatchoperations').v1; @@ -48,9 +50,24 @@ function main(projectId, jobId) { name, }; - // Run request - await client.deleteJob(request); - console.log(`Deleted job: ${name}`); + try { + // Run request + await client.deleteJob(request); + console.log(`Deleted job: ${name}`); + } catch (error) { + console.error( + `Error deleting batch jobs for jobId ${jobId}:`, + error.message + ); + + if (error.code === 5) { + // NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist. + console.error( + `Ensure the job '${jobId}' exists in project '${projectId}'.` + ); + } + throw error; + } } deleteJob(); diff --git a/storagebatchoperations/getJob.js b/storagebatchoperations/getJob.js index dbe4948c98..f7478f1d12 100644 --- a/storagebatchoperations/getJob.js +++ b/storagebatchoperations/getJob.js @@ -24,15 +24,18 @@ function main(projectId, jobId) { // [START storage_batch_get_job] /** - * TODO(developer): Uncomment these variables before running the sample. + * Retrieves details of a specific batch job instance. + * + * This operation is used to retrieve the detailed current state, execution status, + * and original configuration of a specific Batch Operation job that was previously + * created for a Google Cloud Storage bucket. + * + * @param {string} projectId Your Google Cloud project ID. + * Example: 'my-project-id' + * @param {string} jobId A unique identifier for this job. + * Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - - // A unique identifier for this job. - // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; - // Imports the Control library const {StorageBatchOperationsClient} = require('@google-cloud/storagebatchoperations').v1; @@ -48,9 +51,29 @@ function main(projectId, jobId) { name, }; - // Run request - const [response] = await client.getJob(request); - console.log(`Got job: ${response.name}`); + try { + // Run request + const [response] = await client.getJob(request); + console.log(`Batch job details for '${jobId}':`); + console.log(` Name: ${response.name}`); + console.log(` State: ${response.state}`); + console.log( + ` Create Time: ${new Date(response.createTime.seconds * 1000).toISOString()}` + ); + } catch (error) { + console.error( + `Error retrieving batch jobs for jobId ${jobId}:`, + error.message + ); + + if (error.code === 5) { + // NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist. + console.error( + `Ensure the job '${jobId}' exists in project '${projectId}'.` + ); + } + throw error; + } } getJob(); diff --git a/storagebatchoperations/listJobs.js b/storagebatchoperations/listJobs.js index fd87266585..a72dbd4878 100644 --- a/storagebatchoperations/listJobs.js +++ b/storagebatchoperations/listJobs.js @@ -24,12 +24,16 @@ function main(projectId) { // [START storage_batch_list_jobs] /** - * TODO(developer): Uncomment these variables before running the sample. + * Lists all Jobs operation is used to query the status and configuration of all + * Storage Batch Operations jobs within a specific Google Cloud project. + * This feature is essential for tasks that affect a large number of objects, + * such as changing storage classes, deleting objects, or running custom functions + * on object metadata. + * + * @param {string} projectId Your Google Cloud project ID. + * Example: 'my-project-id' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - // Imports the Control library const {StorageBatchOperationsClient} = require('@google-cloud/storagebatchoperations').v1; @@ -45,10 +49,26 @@ function main(projectId) { parent, }; - // Run request - const [response] = await client.listJobs(request); - for (const job of response) { - console.log(job.name); + try { + // Run request. The response is an array where the first element is the list of jobs. + const [response] = await client.listJobs(request); + if (response && response.length > 0) { + console.log( + `Found ${response.length} batch jobs for project: ${projectId}` + ); + for (const job of response) { + console.log(job.name); + } + } else { + // Case: Successful but empty list (No batch jobs found) + console.log(`No batch jobs found for project: ${projectId}.`); + } + } catch (error) { + console.error( + `Error listing batch jobs for project ${projectId}:`, + error.message + ); + throw error; } } diff --git a/storagebatchoperations/quickstart.js b/storagebatchoperations/quickstart.js index 7fd6e42a0d..4a022041e1 100644 --- a/storagebatchoperations/quickstart.js +++ b/storagebatchoperations/quickstart.js @@ -29,13 +29,17 @@ async function main(projectId, jobId) { require('@google-cloud/storagebatchoperations').v1; /** - * TODO(developer): Uncomment the following lines before running the sample. + * Retrieves details of a specific batch job instance. + * + * This operation is used to retrieve the detailed current state, execution status, + * and original configuration of a specific Batch Operation job that was previously + * created for a Google Cloud Storage bucket. + * + * @param {string} projectId Your Google Cloud project ID. + * Example: 'my-project-id' + * @param {string} jobId A unique identifier for this job. + * Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5' */ - // Your Google Cloud project ID. - // const projectId = 'my-project-id'; - - // A unique identifier for this job. - // const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'; // Creates a client const client = new StorageBatchOperationsClient(); @@ -48,9 +52,29 @@ async function main(projectId, jobId) { name, }; - // Run request - const [response] = await client.getJob(request); - console.log(`Got job: ${response.name}`); + try { + // Run request + const [response] = await client.getJob(request); + console.log(`Batch job details for '${jobId}':`); + console.log(` Name: ${response.name}`); + console.log(` State: ${response.state}`); + console.log( + ` Create Time: ${new Date(response.createTime.seconds * 1000).toISOString()}` + ); + } catch (error) { + console.error( + `Error retrieving batch jobs for jobId ${jobId}:`, + error.message + ); + + if (error.code === 5) { + // NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist. + console.error( + `Ensure the job '${jobId}' exists in project '${projectId}'.` + ); + } + throw error; + } } quickstart(); // [END storage_batch_quickstart] diff --git a/storagebatchoperations/system-test/storagebatchoperations.test.js b/storagebatchoperations/system-test/storagebatchoperations.test.js index 42113a0286..2c05879bc6 100644 --- a/storagebatchoperations/system-test/storagebatchoperations.test.js +++ b/storagebatchoperations/system-test/storagebatchoperations.test.js @@ -22,7 +22,7 @@ const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); const projectId = process.env.GCLOUD_PROJECT; const bucketPrefix = 'sbo-samples'; const bucketName = `${bucketPrefix}-${uuid.v4()}`; -const storage = new Storage(); +const storage = new Storage(projectId); const bucket = new Bucket(storage, bucketName); const jobId = uuid.v4(); const jobName = `projects/${projectId}/locations/global/jobs/${jobId}`; @@ -58,24 +58,36 @@ describe('Batch Operations', () => { it('should run quickstart', async () => { const output = execSync(`node quickstart.js ${projectId} ${jobId}`); - assert.match(output, /Got job:/); + const detailsHeader = `Batch job details for '${jobId}':`; + assert.match(output, new RegExp(detailsHeader)); + assert.match(output, /Name:/); assert.match(output, new RegExp(jobName)); + assert.match(output, /State:/); + assert.match(output, /Create Time:/); }); it('should get a job', async () => { const output = execSync(`node getJob.js ${projectId} ${jobId}`); - assert.match(output, /Got job:/); + const detailsHeader = `Batch job details for '${jobId}':`; + assert.match(output, new RegExp(detailsHeader)); + assert.match(output, /Name:/); assert.match(output, new RegExp(jobName)); + assert.match(output, /State:/); + assert.match(output, /Create Time:/); }); - it('should cancel a job', async () => { + it('should cancel a job (or gracefully handle terminal state)', async () => { try { const output = execSync(`node cancelJob.js ${projectId} ${jobId}`); assert.match(output, /Cancelled job:/); assert.match(output, new RegExp(jobName)); } catch (error) { // This might be expected if the job completed quickly or failed creation - assert.match(error.message, /INFO: cancelJob threw: /); + const errorMessage = error.stderr.toString(); + assert.match( + errorMessage, + /9 FAILED_PRECONDITION: Job run.* is in a terminal state and can not be changed./ + ); } });