Skip to content

Commit 7dceecc

Browse files
committed
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.
1 parent 7b85f34 commit 7dceecc

File tree

7 files changed

+191
-69
lines changed

7 files changed

+191
-69
lines changed

storagebatchoperations/cancelJob.js

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@
2323

2424
function main(projectId, jobId) {
2525
// [START storage_batch_cancel_job]
26+
2627
/**
27-
* TODO(developer): Uncomment these variables before running the sample.
28+
* Cancel a batch job instance.
29+
*
30+
* The operation to cancel a batch job instance in Google Cloud Storage (GCS) is used to stop
31+
* a running or queued asynchronous task that is currently processing a large number of GCS objects.
32+
*
33+
* @param {string} projectId The Google Cloud project ID.
34+
* Example: 'my-project-id'
35+
* @param {string} jobId A unique identifier for this job.
36+
* Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'
2837
*/
2938

30-
// Your Google Cloud project ID.
31-
// const projectId = 'my-project-id';
32-
33-
// A unique identifier for this job.
34-
// const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5';
35-
3639
// Imports the Control library
3740
const {StorageBatchOperationsClient} =
3841
require('@google-cloud/storagebatchoperations').v1;
@@ -52,13 +55,30 @@ function main(projectId, jobId) {
5255
try {
5356
await client.cancelJob(request);
5457
console.log(`Cancelled job: ${name}`);
55-
} catch (err) {
58+
} catch (error) {
5659
// This might be expected if the job completed quickly or failed creation
57-
console.log(`INFO: cancelJob threw: ${err.message}`);
60+
console.error(
61+
`Error canceling batch jobs for jobId ${jobId}:`,
62+
error.message
63+
);
64+
65+
if (error.code === 5) {
66+
// NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist.
67+
console.error(
68+
`Ensure the job '${jobId}' exists in project '${projectId}'.`
69+
);
70+
} else if (error.code === 9) {
71+
// FAILED_PRECONDITION (gRPC code 9) can occur if the job is already being cancelled
72+
// or is not in a RUNNING state that allows the cancel operation.
73+
console.error(
74+
`Batch job '${jobId}' may not be in a state that allows canceling (e.g., must be RUNNING).`
75+
);
76+
}
77+
throw error;
5878
}
5979
}
6080

61-
cancelJob().catch(console.error);
81+
cancelJob();
6282
// [END storage_batch_cancel_job]
6383
}
6484

storagebatchoperations/createJob.js

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,20 @@
2323

2424
function main(projectId, jobId, bucketName, objectPrefix) {
2525
// [START storage_batch_create_job]
26+
2627
/**
27-
* TODO(developer): Uncomment these variables before running the sample.
28+
* Create a new batch job instance.
29+
*
30+
* @param {string} projectId Your Google Cloud project ID.
31+
* Example: 'my-project-id'
32+
* @param {string} bucketName The name of your GCS bucket.
33+
* Example: 'your-gcp-bucket-name'
34+
* @param {string} jobId A unique identifier for this job.
35+
* Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'
36+
* @param {string} objectPrefix The prefix of objects to include in the operation.
37+
* Example: 'prefix1'
2838
*/
2939

30-
// Your Google Cloud project ID.
31-
// const projectId = 'my-project-id';
32-
33-
// The name of your GCS bucket
34-
// const bucketName = 'bucketName';
35-
36-
// A unique identifier for this job.
37-
// const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5';
38-
39-
// The prefix of objects to include in the operation.
40-
// const objectPrefix = 'prefix1';
41-
4240
// Imports the Control library
4341
const {StorageBatchOperationsClient} =
4442
require('@google-cloud/storagebatchoperations').v1;
@@ -70,10 +68,18 @@ function main(projectId, jobId, bucketName, objectPrefix) {
7068
},
7169
};
7270

73-
// Run request
74-
const [operation] = await client.createJob(request);
75-
const [response] = await operation.promise();
76-
console.log(`Created job: ${response.name}`);
71+
try {
72+
// Run the request, which returns an Operation object
73+
const [operation] = await client.createJob(request);
74+
console.log(`Waiting for operation ${operation.name} to complete...`);
75+
76+
// Wait for the operation to complete and get the final resource
77+
const [response] = await operation.promise();
78+
console.log(`Created job: ${response.name}`);
79+
} catch (error) {
80+
console.error('Failed to create batch job:', error.message);
81+
throw error;
82+
}
7783
}
7884

7985
createJob();

storagebatchoperations/deleteJob.js

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@
2424
function main(projectId, jobId) {
2525
// [START storage_batch_delete_job]
2626
/**
27-
* TODO(developer): Uncomment these variables before running the sample.
27+
* Delete a batch job instance.
28+
*
29+
* This operation is used to remove a completed, failed, or cancelled Batch Operation
30+
* job from the system's list. It is essentially a cleanup action.
31+
*
32+
* @param {string} projectId Your Google Cloud project ID.
33+
* Example: 'my-project-id'
34+
* @param {string} jobId A unique identifier for this job.
35+
* Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'
2836
*/
2937

30-
// Your Google Cloud project ID.
31-
// const projectId = 'my-project-id';
32-
33-
// A unique identifier for this job.
34-
// const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5';
35-
3638
// Imports the Control library
3739
const {StorageBatchOperationsClient} =
3840
require('@google-cloud/storagebatchoperations').v1;
@@ -48,9 +50,24 @@ function main(projectId, jobId) {
4850
name,
4951
};
5052

51-
// Run request
52-
await client.deleteJob(request);
53-
console.log(`Deleted job: ${name}`);
53+
try {
54+
// Run request
55+
await client.deleteJob(request);
56+
console.log(`Deleted job: ${name}`);
57+
} catch (error) {
58+
console.error(
59+
`Error deleting batch jobs for jobId ${jobId}:`,
60+
error.message
61+
);
62+
63+
if (error.code === 5) {
64+
// NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist.
65+
console.error(
66+
`Ensure the job '${jobId}' exists in project '${projectId}'.`
67+
);
68+
}
69+
throw error;
70+
}
5471
}
5572

5673
deleteJob();

storagebatchoperations/getJob.js

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,18 @@
2424
function main(projectId, jobId) {
2525
// [START storage_batch_get_job]
2626
/**
27-
* TODO(developer): Uncomment these variables before running the sample.
27+
* Retrieves details of a specific batch job instance.
28+
*
29+
* This operation is used to retrieve the detailed current state, execution status,
30+
* and original configuration of a specific Batch Operation job that was previously
31+
* created for a Google Cloud Storage bucket.
32+
*
33+
* @param {string} projectId Your Google Cloud project ID.
34+
* Example: 'my-project-id'
35+
* @param {string} jobId A unique identifier for this job.
36+
* Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'
2837
*/
2938

30-
// Your Google Cloud project ID.
31-
// const projectId = 'my-project-id';
32-
33-
// A unique identifier for this job.
34-
// const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5';
35-
3639
// Imports the Control library
3740
const {StorageBatchOperationsClient} =
3841
require('@google-cloud/storagebatchoperations').v1;
@@ -48,9 +51,29 @@ function main(projectId, jobId) {
4851
name,
4952
};
5053

51-
// Run request
52-
const [response] = await client.getJob(request);
53-
console.log(`Got job: ${response.name}`);
54+
try {
55+
// Run request
56+
const [response] = await client.getJob(request);
57+
console.log(`Batch job details for '${jobId}':`);
58+
console.log(` Name: ${response.name}`);
59+
console.log(` State: ${response.state}`);
60+
console.log(
61+
` Create Time: ${new Date(response.createTime.seconds * 1000).toISOString()}`
62+
);
63+
} catch (error) {
64+
console.error(
65+
`Error retrieving batch jobs for jobId ${jobId}:`,
66+
error.message
67+
);
68+
69+
if (error.code === 5) {
70+
// NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist.
71+
console.error(
72+
`Ensure the job '${jobId}' exists in project '${projectId}'.`
73+
);
74+
}
75+
throw error;
76+
}
5477
}
5578

5679
getJob();

storagebatchoperations/listJobs.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@
2424
function main(projectId) {
2525
// [START storage_batch_list_jobs]
2626
/**
27-
* TODO(developer): Uncomment these variables before running the sample.
27+
* Lists all Jobs operation is used to query the status and configuration of all
28+
* Storage Batch Operations jobs within a specific Google Cloud project.
29+
* This feature is essential for tasks that affect a large number of objects,
30+
* such as changing storage classes, deleting objects, or running custom functions
31+
* on object metadata.
32+
*
33+
* @param {string} projectId Your Google Cloud project ID.
34+
* Example: 'my-project-id'
2835
*/
2936

30-
// Your Google Cloud project ID.
31-
// const projectId = 'my-project-id';
32-
3337
// Imports the Control library
3438
const {StorageBatchOperationsClient} =
3539
require('@google-cloud/storagebatchoperations').v1;
@@ -45,10 +49,26 @@ function main(projectId) {
4549
parent,
4650
};
4751

48-
// Run request
49-
const [response] = await client.listJobs(request);
50-
for (const job of response) {
51-
console.log(job.name);
52+
try {
53+
// Run request. The response is an array where the first element is the list of jobs.
54+
const [response] = await client.listJobs(request);
55+
if (response && response.length > 0) {
56+
console.log(
57+
`Found ${response.length} batch jobs for project: ${projectId}`
58+
);
59+
for (const job of response) {
60+
console.log(job.name);
61+
}
62+
} else {
63+
// Case: Successful but empty list (No batch jobs found)
64+
console.log(`No batch jobs found for project: ${projectId}.`);
65+
}
66+
} catch (error) {
67+
console.error(
68+
`Error listing batch jobs for project ${projectId}:`,
69+
error.message
70+
);
71+
throw error;
5272
}
5373
}
5474

storagebatchoperations/quickstart.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ async function main(projectId, jobId) {
2929
require('@google-cloud/storagebatchoperations').v1;
3030

3131
/**
32-
* TODO(developer): Uncomment the following lines before running the sample.
32+
* Retrieves details of a specific batch job instance.
33+
*
34+
* This operation is used to retrieve the detailed current state, execution status,
35+
* and original configuration of a specific Batch Operation job that was previously
36+
* created for a Google Cloud Storage bucket.
37+
*
38+
* @param {string} projectId Your Google Cloud project ID.
39+
* Example: 'my-project-id'
40+
* @param {string} jobId A unique identifier for this job.
41+
* Example: '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5'
3342
*/
34-
// Your Google Cloud project ID.
35-
// const projectId = 'my-project-id';
36-
37-
// A unique identifier for this job.
38-
// const jobId = '94d60cc1-2d95-41c5-b6e3-ff66cd3532d5';
3943

4044
// Creates a client
4145
const client = new StorageBatchOperationsClient();
@@ -48,9 +52,29 @@ async function main(projectId, jobId) {
4852
name,
4953
};
5054

51-
// Run request
52-
const [response] = await client.getJob(request);
53-
console.log(`Got job: ${response.name}`);
55+
try {
56+
// Run request
57+
const [response] = await client.getJob(request);
58+
console.log(`Batch job details for '${jobId}':`);
59+
console.log(` Name: ${response.name}`);
60+
console.log(` State: ${response.state}`);
61+
console.log(
62+
` Create Time: ${new Date(response.createTime.seconds * 1000).toISOString()}`
63+
);
64+
} catch (error) {
65+
console.error(
66+
`Error retrieving batch jobs for jobId ${jobId}:`,
67+
error.message
68+
);
69+
70+
if (error.code === 5) {
71+
// NOT_FOUND (gRPC code 5) error can occur if the batch job does not exist.
72+
console.error(
73+
`Ensure the job '${jobId}' exists in project '${projectId}'.`
74+
);
75+
}
76+
throw error;
77+
}
5478
}
5579
quickstart();
5680
// [END storage_batch_quickstart]

storagebatchoperations/system-test/storagebatchoperations.test.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'});
2222
const projectId = process.env.GCLOUD_PROJECT;
2323
const bucketPrefix = 'sbo-samples';
2424
const bucketName = `${bucketPrefix}-${uuid.v4()}`;
25-
const storage = new Storage();
25+
const storage = new Storage(projectId);
2626
const bucket = new Bucket(storage, bucketName);
2727
const jobId = uuid.v4();
2828
const jobName = `projects/${projectId}/locations/global/jobs/${jobId}`;
@@ -58,24 +58,36 @@ describe('Batch Operations', () => {
5858

5959
it('should run quickstart', async () => {
6060
const output = execSync(`node quickstart.js ${projectId} ${jobId}`);
61-
assert.match(output, /Got job:/);
61+
const detailsHeader = `Batch job details for '${jobId}':`;
62+
assert.match(output, new RegExp(detailsHeader));
63+
assert.match(output, /Name:/);
6264
assert.match(output, new RegExp(jobName));
65+
assert.match(output, /State:/);
66+
assert.match(output, /Create Time:/);
6367
});
6468

6569
it('should get a job', async () => {
6670
const output = execSync(`node getJob.js ${projectId} ${jobId}`);
67-
assert.match(output, /Got job:/);
71+
const detailsHeader = `Batch job details for '${jobId}':`;
72+
assert.match(output, new RegExp(detailsHeader));
73+
assert.match(output, /Name:/);
6874
assert.match(output, new RegExp(jobName));
75+
assert.match(output, /State:/);
76+
assert.match(output, /Create Time:/);
6977
});
7078

71-
it('should cancel a job', async () => {
79+
it('should cancel a job (or gracefully handle terminal state)', async () => {
7280
try {
7381
const output = execSync(`node cancelJob.js ${projectId} ${jobId}`);
7482
assert.match(output, /Cancelled job:/);
7583
assert.match(output, new RegExp(jobName));
7684
} catch (error) {
7785
// This might be expected if the job completed quickly or failed creation
78-
assert.match(error.message, /INFO: cancelJob threw: /);
86+
const errorMessage = error.stderr.toString();
87+
assert.match(
88+
errorMessage,
89+
/9 FAILED_PRECONDITION: Job run.* is in a terminal state and can not be changed./
90+
);
7991
}
8092
});
8193

0 commit comments

Comments
 (0)