Skip to content

Conversation

@khilan-crest
Copy link

Adding expiretime samples

Fixes #

Note: Before submitting a pull request, please open an issue for discussion if you are not associated with Google.

Checklist

  • I have followed guidelines from CONTRIBUTING.MD and Samples Style Guide
  • Tests pass: npm test (see Testing)
  • Lint pass: npm run lint (see Style)
  • Required CI tests pass (see CI testing)
  • These samples need a new API enabled in testing projects to pass (let us know which ones)
  • These samples need a new/updated env vars in testing projects set to pass (let us know which ones)
  • This pull request is from a branch created directly off of GoogleCloudPlatform/nodejs-docs-samples. Not a fork.
  • This sample adds a new sample directory, and I updated the CODEOWNERS file with the codeowners for this sample
  • This sample adds a new sample directory, and I created GitHub Actions workflow for this sample
  • This sample adds a new Product API, and I updated the Blunderbuss issue/PR auto-assigner with the codeowners for this sample
  • Please merge this PR for me once it is approved

Note: Any check with (dev), (experimental), or (legacy) can be ignored and should not block your PR from merging (see CI testing).

@khilan-crest khilan-crest requested review from a team as code owners January 21, 2026 08:33
@product-auto-label product-auto-label bot added api: secretmanager Issues related to the Secret Manager API. samples Issues that are directly related to samples. labels Jan 21, 2026
@snippet-bot
Copy link

snippet-bot bot commented Jan 21, 2026

Here is the summary of changes.

You are about to add 17 region tags.

This comment is generated by snippet-bot.
If you find problems with this result, please file an issue at:
https://github.com/googleapis/repo-automation-bots/issues.
To update this comment, add snippet-bot:force-run label or use the checkbox below:

  • Refresh this comment

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @khilan-crest, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Google Cloud Secret Manager Node.js samples by introducing a suite of new functionalities. The additions focus on advanced secret management features, including the ability to define and manage secret expiration, utilize customer-managed encryption keys, and configure custom replication policies. Furthermore, the PR provides new ways to query and organize secrets through filtering and tag binding management. These updates aim to provide developers with clearer examples and broader coverage of the Secret Manager API's capabilities.

Highlights

  • New Secret Expiration Samples: Added samples demonstrating how to create secrets with expiration times, update existing secret expiration times, and remove expiration from secrets, covering both global and regional Secret Manager instances.
  • Customer-Managed Encryption Keys (CMEK) Samples: Introduced samples for creating secrets encrypted with Customer-Managed Encryption Keys (CMEK) for both global and regional Secret Manager services.
  • User-Managed Replication Policy Sample: A new sample was added to illustrate the creation of secrets with user-managed replication policies, allowing specification of replica locations and an optional Time-To-Live (TTL).
  • Filtering and Listing Samples: New samples provide functionality to list secrets and secret versions using filters, and to list and detach tag bindings from secrets, available for both global and regional contexts.
  • Comprehensive Test Coverage: The test suite has been significantly expanded to include tests for all newly added samples, ensuring their correctness and proper cleanup of created resources.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds several new samples for Secret Manager, demonstrating features like expiration times, CMEK, user-managed replication, and tag management. The changes are comprehensive and cover both global and regional secrets.

My review has found a few issues:

  • A recurring critical bug where asynchronous operations are not awaited, which would cause scripts to terminate prematurely.
  • A bug in one sample where a parameter is passed with an incorrect type.
  • Some opportunities for code simplification by removing unnecessarily nested functions.
  • A minor inconsistency in code formatting.

I've provided specific suggestions to address these points in the comments below. Once these are addressed, the new samples will be much more robust and easier to understand.

Comment on lines +39 to +54
async function deleteRegionalSecretExpiration() {
// Update the secret to remove the expiration time
const [secret] = await client.updateSecret({
secret: {
name: name,
// No expireTime field specified, which will clear it
},
updateMask: {
paths: ['expire_time'],
},
});

console.log(`Removed expiration from secret ${secret.name}`);
}

deleteRegionalSecretExpiration();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to deleteRegionalSecretExpiration() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the deleteRegionalSecretExpiration function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    // Update the secret to remove the expiration time
    const [secret] = await client.updateSecret({
      secret: {
        name: name,
        // No expireTime field specified, which will clear it
      },
      updateMask: {
        paths: ['expire_time'],
      },
    });

    console.log(`Removed expiration from secret ${secret.name}`);

Copy link
Author

@khilan-crest khilan-crest Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +37 to +59
async function createRegionalSecretWithExpiration() {
// Calculate expiration time (1 hour from now)
const expireTime = new Date();
expireTime.setHours(expireTime.getHours() + 1);

// Create the secret with automatic replication and expiration time
const [secret] = await client.createSecret({
parent: parent,
secretId: secretId,
secret: {
expireTime: {
seconds: Math.floor(expireTime.getTime() / 1000),
nanos: (expireTime.getTime() % 1000) * 1000000,
},
},
});

console.log(
`Created secret ${secret.name} with expiration time ${expireTime.toISOString()}`
);
}

createRegionalSecretWithExpiration();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to createRegionalSecretWithExpiration() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the createRegionalSecretWithExpiration function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    // Calculate expiration time (1 hour from now)
    const expireTime = new Date();
    expireTime.setHours(expireTime.getHours() + 1);

    // Create the secret with automatic replication and expiration time
    const [secret] = await client.createSecret({
      parent: parent,
      secretId: secretId,
      secret: {
        expireTime: {
          seconds: Math.floor(expireTime.getTime() / 1000),
          nanos: (expireTime.getTime() % 1000) * 1000000,
        },
      },
    });

    console.log(
      `Created secret ${secret.name} with expiration time ${expireTime.toISOString()}`
    );

Copy link
Author

@khilan-crest khilan-crest Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +30 to +55
async function updateSecretExpiration() {
// Calculate new expiration time (2 hours from now)
const newExpireTime = new Date();
newExpireTime.setHours(newExpireTime.getHours() + 2);

// Update the expire_time
const [response] = await client.updateSecret({
secret: {
name: name,
expireTime: {
seconds: Math.floor(newExpireTime.getTime() / 1000),
nanos: (newExpireTime.getTime() % 1000) * 1000000,
},
},
updateMask: {
paths: ['expire_time'],
},
});

// Print the updated secret name
console.log(
`Updated secret ${response.name} expiration time to ${newExpireTime.toISOString()}`
);
}

updateSecretExpiration();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to updateSecretExpiration() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the updateSecretExpiration function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    // Calculate new expiration time (2 hours from now)
    const newExpireTime = new Date();
    newExpireTime.setHours(newExpireTime.getHours() + 2);

    // Update the expire_time
    const [response] = await client.updateSecret({
      secret: {
        name: name,
        expireTime: {
          seconds: Math.floor(newExpireTime.getTime() / 1000),
          nanos: (newExpireTime.getTime() % 1000) * 1000000,
        },
      },
      updateMask: {
        paths: ['expire_time'],
      },
    });

    // Print the updated secret name
    console.log(
      `Updated secret ${response.name} expiration time to ${newExpireTime.toISOString()}`
    );

Copy link
Author

@khilan-crest khilan-crest Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +33 to +56
async function createUmmrSecret() {
// Create the secret configuration
const secretConfig = {
replication: {
userManaged: {
replicas: locations.map(location => ({location})),
},
},
ttl: {
seconds: ttl,
},
};

// Create the secret
const [secret] = await client.createSecret({
parent: parent,
secretId: secretId,
secret: secretConfig,
});

console.log(`Created secret: ${secret.name}`);
}

createUmmrSecret();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to createUmmrSecret() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the createUmmrSecret function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    // Create the secret configuration
    const secretConfig = {
      replication: {
        userManaged: {
          replicas: locations.map(location => ({location})),
        },
      },
      ttl: {
        seconds: ttl,
      },
    };

    // Create the secret
    const [secret] = await client.createSecret({
      parent: parent,
      secretId: secretId,
      secret: secretConfig,
    });

    console.log(`Created secret: ${secret.name}`);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +39 to +63
async function updateRegionalSecretExpiration() {
// Calculate new expiration time (2 hours from now)
const newExpireTime = new Date();
newExpireTime.setHours(newExpireTime.getHours() + 2);

// Update the secret with the new expiration time
const [secret] = await client.updateSecret({
secret: {
name: name,
expireTime: {
seconds: Math.floor(newExpireTime.getTime() / 1000),
nanos: (newExpireTime.getTime() % 1000) * 1000000,
},
},
updateMask: {
paths: ['expire_time'],
},
});

console.log(
`Updated secret ${secret.name} expiration time to ${newExpireTime.toISOString()}`
);
}

updateRegionalSecretExpiration();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to updateRegionalSecretExpiration() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the updateRegionalSecretExpiration function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    // Calculate new expiration time (2 hours from now)
    const newExpireTime = new Date();
    newExpireTime.setHours(newExpireTime.getHours() + 2);

    // Update the secret with the new expiration time
    const [secret] = await client.updateSecret({
      secret: {
        name: name,
        expireTime: {
          seconds: Math.floor(newExpireTime.getTime() / 1000),
          nanos: (newExpireTime.getTime() % 1000) * 1000000,
        },
      },
      updateMask: {
        paths: ['expire_time'],
      },
    });

    console.log(
      `Updated secret ${secret.name} expiration time to ${newExpireTime.toISOString()}`
    );

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +39 to +50
async function listRegionalSecretVersionsWithFilter() {
const [versions] = await client.listSecretVersions({
parent: parent,
filter: filterStr,
});

versions.forEach(version => {
console.log(`Found version: ${version.name}`);
});
}

listRegionalSecretVersionsWithFilter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to listRegionalSecretVersionsWithFilter() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the listRegionalSecretVersionsWithFilter function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    const [versions] = await client.listSecretVersions({
      parent: parent,
      filter: filterStr,
    });

    versions.forEach(version => {
      console.log(`Found version: ${version.name}`);
    });

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

Comment on lines +38 to +49
async function listRegionalSecretsWithFilter() {
const [secrets] = await client.listSecrets({
parent: parent,
filter: filterStr,
});

secrets.forEach(secret => {
console.log(`Found secret: ${secret.name}`);
});
}

listRegionalSecretsWithFilter();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The main function is async, but the call to listRegionalSecretsWithFilter() is not awaited. This can lead to the script exiting before the asynchronous operation completes.

Additionally, the listRegionalSecretsWithFilter function is defined and then immediately called only once. You can simplify the code by removing this inner function and moving its logic directly into the main function. This improves readability and fixes the bug.

    const [secrets] = await client.listSecrets({
      parent: parent,
      filter: filterStr,
    });

    secrets.forEach(secret => {
      console.log(`Found secret: ${secret.name}`);
    });

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All samples contains inner function within main which is showing main functionality of the sample. For consistency with other samples this is not required.

},
},
ttl: {
seconds: ttl,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The createSecret API expects the seconds field for ttl to be a number. The current code passes the ttl string argument directly. Based on the example in the comment ('7776000s') and the test, this will be a string with an 's' suffix, which will cause an error. You should parse the integer value from the ttl argument.

        seconds: parseInt(ttl, 10),

Copy link
Author

@khilan-crest khilan-crest Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done
Updated code to pass int not str

Comment on lines 60 to 61
}
// [END secretmanager_create_regional_secret_with_expire_time]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other samples, the // [END ...] region tag should be placed inside the main function, just before its closing brace.

  // [END secretmanager_create_regional_secret_with_expire_time]
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 57 to 58
}
// [END secretmanager_create_secret_with_expiration]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other samples, the // [END ...] region tag should be placed inside the main function, just before its closing brace.

  // [END secretmanager_create_secret_with_expiration]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: secretmanager Issues related to the Secret Manager API. samples Issues that are directly related to samples.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant