diff --git a/.github/workflows/generation_check.yaml b/.github/workflows/generation_check.yaml new file mode 100644 index 000000000000..4e8c12451f2f --- /dev/null +++ b/.github/workflows/generation_check.yaml @@ -0,0 +1,75 @@ +--- +name: generation-check + +on: + push: + branches: + - main + pull_request: + # Allows manual triggering for debugging purpose. + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read # Read repository contents + +jobs: + regeneration: + name: Regenerate and verify clean git tree + runs-on: ubuntu-latest + permissions: + contents: read # Read repository contents + issues: write # Required for creating issue on main branch failure + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + id: changes + with: + filters: | + librarian: + - 'librarian.yaml' + # Version of librarian is pulled from librarian.yaml, + # action is pinned to commit SHA to satisfy blanket security policy. + - uses: googleapis/librarian@35997441eafc2b02716804f9baba1e3f04f8a44f # v0.31.1 + if: steps.changes.outputs.librarian == 'true' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + - name: Use Node.js 22 + if: steps.changes.outputs.librarian == 'true' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 + with: + node-version: 22 + - name: Setup pnpm + if: steps.changes.outputs.librarian == 'true' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: ^7.0.0 + - name: Install tools + if: steps.changes.outputs.librarian == 'true' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + run: librarian install + - name: Regenerate + if: steps.changes.outputs.librarian == 'true' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + run: | + librarian generate --all + if [ -n "$(git status --porcelain)" ]; then + git status + echo "==================== GIT DIFF ====================" + git diff + echo "==================================================" + echo "Regeneration failed. Please run 'librarian generate --all' to update the generated files." + exit 1 + fi + - name: Create issue if previous step fails + if: ${{ failure() && github.ref == 'refs/heads/main' }} + uses: googleapis/librarian/.github/actions/create-issue-on-failure@35997441eafc2b02716804f9baba1e3f04f8a44f # v0.31.1 + with: + title: "Librarian generate diff check failed on main branch" + body: | + The librarian generate diff check failed on main branch. + + To keep the `main` branch healthy, please consider **reverting the triggering change** first. Once the revert is merged, you can investigate the failure and submit a new PR with the fix. + + Please check the logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/librarian_tidy.yml b/.github/workflows/librarian_tidy.yml new file mode 100644 index 000000000000..e830d1d3f187 --- /dev/null +++ b/.github/workflows/librarian_tidy.yml @@ -0,0 +1,47 @@ +name: librarian tidy + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + tidy-check: + name: Verify librarian.yaml is tidy + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + id: changes + with: + filters: | + librarian: + - 'librarian.yaml' + # Version of librarian is pulled from librarian.yaml, + # action is pinned to commit SHA to satisfy blanket security policy. + - uses: googleapis/librarian@35997441eafc2b02716804f9baba1e3f04f8a44f # v0.31.1 + + - name: Run librarian tidy + if: steps.changes.outputs.librarian == 'true' + run: librarian tidy + + - name: Check for diff + if: steps.changes.outputs.librarian == 'true' + run: | + if ! git diff --exit-code; then + V=$(librarian config get version 2>/dev/null || echo "latest") + echo "librarian.yaml is not tidy. Please run:" + echo "" + echo " librarian tidy" + echo "" + echo "to tidy librarian.yaml and commit the changes." + exit 1 + fi diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1986fffc0f38..48aef931009f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -2,15 +2,15 @@ "core/common": "8.0.0", "core/dev-packages/jsdoc-fresh": "6.0.0", "core/dev-packages/jsdoc-region-tag": "5.0.0", - "core/dev-packages/pack-n-play": "5.0.0", + "core/dev-packages/pack-n-play": "5.0.1", "core/generator/gapic-generator-typescript": "5.0.0", "core/packages/gapic-node-processing": "0.2.0", "core/packages/gax": "6.0.0", "core/packages/gaxios": "7.3.0", - "core/packages/gcp-metadata": "9.0.0", + "core/packages/gcp-metadata": "9.0.1", "core/packages/google-auth-library-nodejs": "11.0.0", "core/packages/logging-utils": "2.0.0", - "core/packages/nodejs-googleapis-common": "9.0.0", + "core/packages/nodejs-googleapis-common": "9.0.1", "core/packages/nodejs-proto-files": "6.0.0", "core/packages/proto3-json-serializer-nodejs": "4.0.0", "core/packages/retry-request": "9.0.0", @@ -21,9 +21,9 @@ "core/precise-date": "6.0.0", "core/projectify": "6.0.0", "core/promisify": "6.0.0", - "handwritten/bigquery": "9.0.0", + "handwritten/bigquery": "9.0.1", "handwritten/bigquery-storage": "6.0.0", - "handwritten/bigtable": "7.0.0", + "handwritten/bigtable": "7.1.0", "handwritten/cloud-profiler": "8.0.0", "handwritten/datastore": "11.0.0", "handwritten/error-reporting": "4.0.0", @@ -104,7 +104,7 @@ "packages/google-cloud-connectors": "0.6.0", "packages/google-cloud-contactcenterinsights": "5.0.0", "packages/google-cloud-contentwarehouse": "3.0.0", - "packages/google-cloud-databasecenter": "0.4.0", + "packages/google-cloud-databasecenter": "0.4.1", "packages/google-cloud-datacatalog": "6.0.0", "packages/google-cloud-datacatalog-lineage": "3.0.0", "packages/google-cloud-datacatalog-lineage-configmanagement": "0.2.0", diff --git a/changelog.json b/changelog.json index f4dc6b25538c..a9e0bf8919e6 100644 --- a/changelog.json +++ b/changelog.json @@ -1,6 +1,113 @@ { "repository": "googleapis/google-cloud-node", "entries": [ + { + "changes": [ + { + "type": "fix", + "sha": "23aee114a947a29f31977aab8de9c1c7ecd62188", + "message": "Upgrade types sinon for databasecenter", + "issues": [ + "9098" + ] + } + ], + "version": "0.4.1", + "language": "JAVASCRIPT", + "artifactName": "@google-cloud/databasecenter", + "id": "c0b55b44-2b73-457b-9e25-8d86ece8b512", + "createTime": "2026-08-05T21:09:31.314Z" + }, + { + "changes": [ + { + "type": "feat", + "sha": "f63f914e69106f581448837f5ee212ab71f85775", + "message": "Split gapic and proto usage from the handwritten library into bigtable-api", + "issues": [ + "8934" + ], + "scope": "bigtable" + } + ], + "version": "7.1.0", + "language": "JAVASCRIPT", + "artifactName": "@google-cloud/bigtable", + "id": "dc784ab8-295a-4e02-8261-f7935abb2788", + "createTime": "2026-08-05T21:09:31.294Z" + }, + { + "changes": [ + { + "type": "fix", + "sha": "7f8e23c9652e34869c2fafe54f63cc63ac9acaa8", + "message": "Remove owlbot configs", + "issues": [ + "8936" + ], + "scope": "bigquery" + } + ], + "version": "9.0.1", + "language": "JAVASCRIPT", + "artifactName": "@google-cloud/bigquery", + "id": "de1dae99-d138-451f-a0db-e716e0003958", + "createTime": "2026-08-05T21:09:31.273Z" + }, + { + "changes": [ + { + "type": "fix", + "sha": "340364f8fab67adaf4e684fb3241de499394bf6b", + "message": "Discovery url handling, error code review helper, and pack-n-play test timeouts", + "issues": [ + "9061" + ], + "scope": "core" + } + ], + "version": "9.0.1", + "language": "JAVASCRIPT", + "artifactName": "googleapis-common", + "id": "2d37b8dd-1417-4f37-ae57-18b166ea395a", + "createTime": "2026-08-05T21:09:31.248Z" + }, + { + "changes": [ + { + "type": "fix", + "sha": "340364f8fab67adaf4e684fb3241de499394bf6b", + "message": "Discovery url handling, error code review helper, and pack-n-play test timeouts", + "issues": [ + "9061" + ], + "scope": "core" + } + ], + "version": "9.0.1", + "language": "JAVASCRIPT", + "artifactName": "gcp-metadata", + "id": "9eda0e2d-73dd-410e-a223-df0621e697d6", + "createTime": "2026-08-05T21:09:31.228Z" + }, + { + "changes": [ + { + "type": "fix", + "sha": "340364f8fab67adaf4e684fb3241de499394bf6b", + "message": "Discovery url handling, error code review helper, and pack-n-play test timeouts", + "issues": [ + "9061" + ], + "scope": "core" + } + ], + "version": "5.0.1", + "language": "JAVASCRIPT", + "artifactName": "pack-n-play", + "id": "80b6e996-31bf-482f-8d5c-02220564552f", + "createTime": "2026-08-05T21:09:31.200Z" + }, { "changes": [ { @@ -88682,5 +88789,5 @@ "createTime": "2023-01-28T04:18:24.718Z" } ], - "updateTime": "2026-08-04T23:49:33.509Z" + "updateTime": "2026-08-05T21:09:31.314Z" } \ No newline at end of file diff --git a/core/dev-packages/pack-n-play/CHANGELOG.md b/core/dev-packages/pack-n-play/CHANGELOG.md index cbfa24ef4098..ff9c3f04cd3c 100644 --- a/core/dev-packages/pack-n-play/CHANGELOG.md +++ b/core/dev-packages/pack-n-play/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog +## [5.0.1](https://github.com/googleapis/google-cloud-node/compare/pack-n-play-v5.0.0...pack-n-play-v5.0.1) (2026-08-05) + + +### Bug Fixes + +* **core:** Discovery url handling, error code review helper, and pack-n-play test timeouts ([#9061](https://github.com/googleapis/google-cloud-node/issues/9061)) ([340364f](https://github.com/googleapis/google-cloud-node/commit/340364f8fab67adaf4e684fb3241de499394bf6b)) + ## [5.0.0](https://github.com/googleapis/google-cloud-node/compare/pack-n-play-v4.2.3...pack-n-play-v5.0.0) (2026-07-29) diff --git a/core/dev-packages/pack-n-play/package.json b/core/dev-packages/pack-n-play/package.json index ad6dc1a6938b..8c52b49721cd 100644 --- a/core/dev-packages/pack-n-play/package.json +++ b/core/dev-packages/pack-n-play/package.json @@ -1,7 +1,7 @@ { "name": "pack-n-play", "description": "Ensure your library installs and compiles", - "version": "5.0.0", + "version": "5.0.1", "author": "Google LLC", "license": "Apache-2.0", "repository": { diff --git a/core/packages/gcp-metadata/CHANGELOG.md b/core/packages/gcp-metadata/CHANGELOG.md index ff0063d2f2a9..4ceb65011936 100644 --- a/core/packages/gcp-metadata/CHANGELOG.md +++ b/core/packages/gcp-metadata/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://www.npmjs.com/package/gcp-metadata?activeTab=versions +## [9.0.1](https://github.com/googleapis/google-cloud-node/compare/gcp-metadata-v9.0.0...gcp-metadata-v9.0.1) (2026-08-05) + + +### Bug Fixes + +* **core:** Discovery url handling, error code review helper, and pack-n-play test timeouts ([#9061](https://github.com/googleapis/google-cloud-node/issues/9061)) ([340364f](https://github.com/googleapis/google-cloud-node/commit/340364f8fab67adaf4e684fb3241de499394bf6b)) + ## [9.0.0](https://github.com/googleapis/google-cloud-node/compare/gcp-metadata-v8.1.4...gcp-metadata-v9.0.0) (2026-07-29) diff --git a/core/packages/gcp-metadata/package.json b/core/packages/gcp-metadata/package.json index 8b26fc8f3ae1..78eb4d77cc95 100644 --- a/core/packages/gcp-metadata/package.json +++ b/core/packages/gcp-metadata/package.json @@ -1,6 +1,6 @@ { "name": "gcp-metadata", - "version": "9.0.0", + "version": "9.0.1", "description": "Get the metadata from a Google Cloud Platform environment", "repository": { "type": "git", diff --git a/core/packages/gcp-metadata/samples/package.json b/core/packages/gcp-metadata/samples/package.json index 5790a1b71927..a7a0992b436f 100644 --- a/core/packages/gcp-metadata/samples/package.json +++ b/core/packages/gcp-metadata/samples/package.json @@ -14,7 +14,7 @@ "test": "mocha" }, "dependencies": { - "gcp-metadata": "^9.0.0" + "gcp-metadata": "^9.0.1" }, "devDependencies": { "chai": "^4.2.0", diff --git a/core/packages/nodejs-googleapis-common/CHANGELOG.md b/core/packages/nodejs-googleapis-common/CHANGELOG.md index 511e86585498..cf43f81713c0 100644 --- a/core/packages/nodejs-googleapis-common/CHANGELOG.md +++ b/core/packages/nodejs-googleapis-common/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://www.npmjs.com/package/nodejs-googleapis-common?activeTab=versions +## [9.0.1](https://github.com/googleapis/google-cloud-node/compare/googleapis-common-v9.0.0...googleapis-common-v9.0.1) (2026-08-05) + + +### Bug Fixes + +* **core:** Discovery url handling, error code review helper, and pack-n-play test timeouts ([#9061](https://github.com/googleapis/google-cloud-node/issues/9061)) ([340364f](https://github.com/googleapis/google-cloud-node/commit/340364f8fab67adaf4e684fb3241de499394bf6b)) + ## [9.0.0](https://github.com/googleapis/google-cloud-node/compare/googleapis-common-v8.0.3...googleapis-common-v9.0.0) (2026-07-29) diff --git a/core/packages/nodejs-googleapis-common/package.json b/core/packages/nodejs-googleapis-common/package.json index 082b61bf7942..2ad882e902af 100644 --- a/core/packages/nodejs-googleapis-common/package.json +++ b/core/packages/nodejs-googleapis-common/package.json @@ -1,6 +1,6 @@ { "name": "googleapis-common", - "version": "9.0.0", + "version": "9.0.1", "description": "A common tooling library used by the googleapis npm module. You probably don't want to use this directly.", "repository": { "type": "git", diff --git a/core/packages/nodejs-googleapis-common/samples/package.json b/core/packages/nodejs-googleapis-common/samples/package.json index 192850261c50..07446f01ac9f 100644 --- a/core/packages/nodejs-googleapis-common/samples/package.json +++ b/core/packages/nodejs-googleapis-common/samples/package.json @@ -15,7 +15,7 @@ "test": "echo \"There are no sample tests 👻\"" }, "dependencies": { - "googleapis-common": "^9.0.0" + "googleapis-common": "^9.0.1" }, "devDependencies": { "mocha": "^8.0.0" diff --git a/handwritten/bigquery/CHANGELOG.md b/handwritten/bigquery/CHANGELOG.md index af977317129a..12b796fd7bec 100644 --- a/handwritten/bigquery/CHANGELOG.md +++ b/handwritten/bigquery/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://www.npmjs.com/package/@google-cloud/bigquery?activeTab=versions +## [9.0.1](https://github.com/googleapis/google-cloud-node/compare/bigquery-v9.0.0...bigquery-v9.0.1) (2026-08-05) + + +### Bug Fixes + +* **bigquery:** Remove owlbot configs ([#8936](https://github.com/googleapis/google-cloud-node/issues/8936)) ([7f8e23c](https://github.com/googleapis/google-cloud-node/commit/7f8e23c9652e34869c2fafe54f63cc63ac9acaa8)) + ## [9.0.0](https://github.com/googleapis/google-cloud-node/compare/bigquery-v8.3.1...bigquery-v9.0.0) (2026-08-04) diff --git a/handwritten/bigquery/package.json b/handwritten/bigquery/package.json index b0d97aa060c1..8c08afd9d8d2 100644 --- a/handwritten/bigquery/package.json +++ b/handwritten/bigquery/package.json @@ -1,7 +1,7 @@ { "name": "@google-cloud/bigquery", "description": "Google BigQuery Client Library for Node.js", - "version": "9.0.0", + "version": "9.0.1", "license": "Apache-2.0", "author": "Google LLC", "engines": { diff --git a/handwritten/bigtable/CHANGELOG.md b/handwritten/bigtable/CHANGELOG.md index 2acbd3b45145..003c12d67c79 100644 --- a/handwritten/bigtable/CHANGELOG.md +++ b/handwritten/bigtable/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://www.npmjs.com/package/@google-cloud/bigtable?activeTab=versions +## [7.1.0](https://github.com/googleapis/google-cloud-node/compare/bigtable-v7.0.0...bigtable-v7.1.0) (2026-08-05) + + +### Features + +* **bigtable:** Split gapic and proto usage from the handwritten library into bigtable-api ([#8934](https://github.com/googleapis/google-cloud-node/issues/8934)) ([f63f914](https://github.com/googleapis/google-cloud-node/commit/f63f914e69106f581448837f5ee212ab71f85775)) + ## [7.0.0](https://github.com/googleapis/google-cloud-node/compare/bigtable-v6.5.1...bigtable-v7.0.0) (2026-08-04) diff --git a/handwritten/bigtable/package.json b/handwritten/bigtable/package.json index 85d6d08be38e..7c5baeebd273 100644 --- a/handwritten/bigtable/package.json +++ b/handwritten/bigtable/package.json @@ -1,6 +1,6 @@ { "name": "@google-cloud/bigtable", - "version": "7.0.0", + "version": "7.1.0", "description": "Cloud Bigtable Client Library for Node.js", "keywords": [ "bigtable", diff --git a/handwritten/storage/src/file.ts b/handwritten/storage/src/file.ts index 786998c5f4e4..95ddce1f0a03 100644 --- a/handwritten/storage/src/file.ts +++ b/handwritten/storage/src/file.ts @@ -390,7 +390,7 @@ export type RenameCallback = MoveCallback; export type RotateEncryptionKeyOptions = string | Buffer | EncryptionKeyOptions; export interface EncryptionKeyOptions { - encryptionKey?: string | Buffer; + encryptionKey?: string | Buffer | null; kmsKeyName?: string; preconditionOpts?: PreconditionOptions; } @@ -439,7 +439,7 @@ const COMPRESSIBLE_MIME_REGEX = new RegExp( export interface FileOptions { crc32cGenerator?: CRC32CValidatorGenerator; - encryptionKey?: string | Buffer; + encryptionKey?: string | Buffer | null; generation?: number | string; restoreToken?: string; kmsKeyName?: string; @@ -498,7 +498,7 @@ export type DownloadCallback = ( export interface DownloadOptions extends CreateReadStreamOptions { destination?: string; - encryptionKey?: string | Buffer; + encryptionKey?: string | Buffer | null; } interface CopyQuery { @@ -673,7 +673,7 @@ class File extends ServiceObject { restoreToken?: string; parent!: Bucket; - private encryptionKey?: string | Buffer; + private encryptionKey?: string | Buffer | null; private encryptionKeyBase64?: string; private encryptionKeyHash?: string; private encryptionKeyInterceptor?: Interceptor; @@ -1175,7 +1175,7 @@ class File extends ServiceObject { this.name = name; - if (options.encryptionKey) { + if (options.encryptionKey !== undefined) { this.setEncryptionKey(options.encryptionKey); } @@ -1452,15 +1452,25 @@ class File extends ServiceObject { const headers: {[index: string]: string | undefined} = {}; - if (this.encryptionKey !== undefined) { + if (this.encryptionKey !== undefined && this.encryptionKey !== null) { headers['x-goog-copy-source-encryption-algorithm'] = 'AES256'; headers['x-goog-copy-source-encryption-key'] = this.encryptionKeyBase64; headers['x-goog-copy-source-encryption-key-sha256'] = this.encryptionKeyHash; } - if (newFile.encryptionKey !== undefined) { - this.setEncryptionKey(newFile.encryptionKey!); + if ( + this.encryptionKey !== undefined && + this.encryptionKey !== null && + newFile.encryptionKey === undefined + ) { + newFile.setEncryptionKey(this.encryptionKey); + } + + if (newFile.encryptionKey !== undefined && newFile.encryptionKey !== null) { + headers['x-goog-encryption-algorithm'] = 'AES256'; + headers['x-goog-encryption-key'] = newFile.encryptionKeyBase64; + headers['x-goog-encryption-key-sha256'] = newFile.encryptionKeyHash; } else if (options.destinationKmsKeyName !== undefined) { query.destinationKmsKeyName = options.destinationKmsKeyName; delete options.destinationKmsKeyName; @@ -1492,10 +1502,12 @@ class File extends ServiceObject { delete options.preconditionOpts; } - this.request( + this.bucket.request( { method: 'POST', - uri: `/rewriteTo/b/${destBucket.name}/o/${encodeURIComponent( + uri: `/o/${encodeURIComponent( + this.name, + )}/rewriteTo/b/${destBucket.name}/o/${encodeURIComponent( newFile.name, )}`, qs: query, @@ -1937,7 +1949,7 @@ class File extends ServiceObject { ), file: this.name, generation: this.generation, - key: this.encryptionKey, + key: this.encryptionKey === null ? undefined : this.encryptionKey, kmsKeyName: this.kmsKeyName, metadata: options.metadata, offset: options.offset, @@ -2465,7 +2477,7 @@ class File extends ServiceObject { const destination = options.destination; delete options.destination; - if (options.encryptionKey) { + if (options.encryptionKey !== undefined) { this.setEncryptionKey(options.encryptionKey); delete options.encryptionKey; } @@ -2555,8 +2567,23 @@ class File extends ServiceObject { * region_tag:storage_download_encrypted_file * Example of downloading an encrypted file: */ - setEncryptionKey(encryptionKey: string | Buffer) { + setEncryptionKey(encryptionKey: string | Buffer | null) { + if (this.encryptionKeyInterceptor) { + const index = this.interceptors.indexOf(this.encryptionKeyInterceptor); + if (index > -1) { + this.interceptors.splice(index, 1); + } + this.encryptionKeyInterceptor = undefined; + } + this.encryptionKey = encryptionKey; + + if (encryptionKey === null || encryptionKey === undefined) { + this.encryptionKeyBase64 = undefined; + this.encryptionKeyHash = undefined; + return this; + } + this.encryptionKeyBase64 = Buffer.from(encryptionKey as string).toString( 'base64', ); @@ -2577,7 +2604,7 @@ class File extends ServiceObject { }, }; - this.interceptors.push(this.encryptionKeyInterceptor!); + this.interceptors.push(this.encryptionKeyInterceptor); return this; } @@ -4195,7 +4222,23 @@ class File extends ServiceObject { options.preconditionOpts?.ifGenerationMatch !== undefined ? {preconditionOpts: options.preconditionOpts} : {}; - this.copy(newFile, copyOptions, callback!); + this.copy(newFile, copyOptions, (err, file, resp) => { + if (!err) { + if (options.encryptionKey !== undefined) { + this.setEncryptionKey(options.encryptionKey); + } else { + this.setEncryptionKey(null); + } + if (options.kmsKeyName !== undefined) { + this.kmsKeyName = options.kmsKeyName; + } else { + this.kmsKeyName = undefined; + } + } + if (callback) { + callback(err, file, resp); + } + }); } save(data: SaveData, options?: SaveOptions): Promise; @@ -4536,7 +4579,7 @@ class File extends ServiceObject { file: this.name, generation: this.generation, isPartialUpload: options.isPartialUpload, - key: this.encryptionKey, + key: this.encryptionKey === null ? undefined : this.encryptionKey, kmsKeyName: this.kmsKeyName, metadata: options.metadata, offset: options.offset, diff --git a/handwritten/storage/system-test/storage.ts b/handwritten/storage/system-test/storage.ts index cf8074d52671..d9351f703732 100644 --- a/handwritten/storage/system-test/storage.ts +++ b/handwritten/storage/system-test/storage.ts @@ -2793,6 +2793,24 @@ describe('storage', function () { const [contents] = await file.download(); assert.strictEqual(contents.toString(), 'secret data'); }); + + it('should copy a CSEK-encrypted file to a standard non-CSEK destination when destination key is null', async () => { + const srcFile = bucket.file('encrypted-source'); + srcFile.setEncryptionKey('a'.repeat(32)); + + await srcFile.save('csek data', { resumable: false }); + + const dstFile = bucket.file('non-csek-destination'); + dstFile.setEncryptionKey(null); + + await srcFile.copy(dstFile); + + const [metadata] = await dstFile.getMetadata(); + assert.strictEqual(metadata.customerEncryption, undefined); + + const [contents] = await dstFile.download(); + assert.strictEqual(contents.toString(), 'csek data'); + }); }); describe.skip('kms keys', () => { diff --git a/handwritten/storage/test/file.ts b/handwritten/storage/test/file.ts index d9f9185a16e8..57b76fc693ba 100644 --- a/handwritten/storage/test/file.ts +++ b/handwritten/storage/test/file.ts @@ -189,6 +189,10 @@ describe('File', () => { let File: any; // eslint-disable-next-line @typescript-eslint/no-explicit-any let file: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let activeFile: any = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let originalCopy: any; const FILE_NAME = 'file-name.png'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -228,6 +232,18 @@ describe('File', () => { './signer': fakeSigner, zlib: fakeZlib, }).File; + + originalCopy = File.prototype.copy; + File.prototype.copy = function (dest: any, options: any, callback: any) { + activeFile = this; + return originalCopy.call(this, dest, options, callback); + }; + }); + + after(() => { + if (originalCopy) { + File.prototype.copy = originalCopy; + } }); beforeEach(() => { @@ -274,6 +290,19 @@ describe('File', () => { specialCharsFile = new File(BUCKET, "special/azAZ!*'()*%/file.jpg"); specialCharsFile.request = util.noop; + activeFile = null; + BUCKET.request = function (reqOpts: any, callback: any) { + if (activeFile && typeof activeFile.request === 'function' && (activeFile.request as any) !== util.noop) { + const prefix = `/o/${encodeURIComponent(activeFile.name)}`; + const modifiedReqOpts = { ...reqOpts }; + if (modifiedReqOpts.uri.startsWith(prefix)) { + modifiedReqOpts.uri = modifiedReqOpts.uri.substring(prefix.length); + } + return activeFile.request(modifiedReqOpts, callback); + } + return Bucket.prototype.request.call(this, reqOpts, callback); + }; + createGunzipOverride = null; handleRespOverride = null; makeWritableStreamOverride = null; @@ -507,6 +536,7 @@ describe('File', () => { }); describe('copy', () => { + it('should throw if no destination is provided', () => { assert.throws(() => { file.copy(); @@ -596,9 +626,7 @@ describe('File', () => { }); it('should set correct headers when file is encrypted', done => { - file.encryptionKey = {}; - file.encryptionKeyBase64 = 'base64'; - file.encryptionKeyHash = 'hash'; + file.setEncryptionKey('sourceKey'); const newFile = new File(BUCKET, 'new-file'); @@ -607,6 +635,9 @@ describe('File', () => { 'x-goog-copy-source-encryption-algorithm': 'AES256', 'x-goog-copy-source-encryption-key': file.encryptionKeyBase64, 'x-goog-copy-source-encryption-key-sha256': file.encryptionKeyHash, + 'x-goog-encryption-algorithm': 'AES256', + 'x-goog-encryption-key': file.encryptionKeyBase64, + 'x-goog-encryption-key-sha256': file.encryptionKeyHash, }); done(); }; @@ -614,12 +645,102 @@ describe('File', () => { file.copy(newFile, assert.ifError); }); - it('should set encryption key on the new File instance', done => { + it('should send destination encryption headers when destination file has an encryption key', done => { + const newFile = new File(BUCKET, 'new-file'); + newFile.setEncryptionKey('destinationKey'); + + file.request = (reqOpts: DecorateRequestOptions) => { + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-algorithm'], + 'AES256' + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key'], + newFile.encryptionKeyBase64 + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key-sha256'], + newFile.encryptionKeyHash + ); + done(); + }; + + file.copy(newFile, assert.ifError); + }); + + it('should not copy encryption key or send destination headers when destination file has null encryption key', done => { + file.setEncryptionKey('sourceKey'); + const expectedSourceKeyBase64 = file.encryptionKeyBase64; + const expectedSourceKeyHash = file.encryptionKeyHash; + + const newFile = new File(BUCKET, 'new-file'); + newFile.setEncryptionKey(null); + + file.request = (reqOpts: DecorateRequestOptions) => { + assert.strictEqual(newFile.encryptionKey, null); + assert.strictEqual(newFile.encryptionKeyBase64, undefined); + assert.strictEqual(newFile.encryptionKeyHash, undefined); + + assert.strictEqual( + reqOpts.headers!['x-goog-copy-source-encryption-algorithm'], + 'AES256' + ); + assert.strictEqual( + reqOpts.headers!['x-goog-copy-source-encryption-key'], + expectedSourceKeyBase64 + ); + assert.strictEqual( + reqOpts.headers!['x-goog-copy-source-encryption-key-sha256'], + expectedSourceKeyHash + ); + + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-algorithm'], + undefined + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key'], + undefined + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key-sha256'], + undefined + ); + + assert.notStrictEqual(file.encryptionKeyInterceptor, undefined); + + done(); + }; + + file.copy(newFile, assert.ifError); + }); + + it('should copy the source key to the destination file object if destination key is undefined', done => { + file.setEncryptionKey('sourceKey'); + const newFile = new File(BUCKET, 'new-file'); - newFile.encryptionKey = 'encryptionKey'; - file.setEncryptionKey = (encryptionKey: {}) => { - assert.strictEqual(encryptionKey, newFile.encryptionKey); + file.request = (reqOpts: DecorateRequestOptions) => { + assert.strictEqual(newFile.encryptionKey, file.encryptionKey); + assert.strictEqual(newFile.encryptionKeyBase64, file.encryptionKeyBase64); + assert.strictEqual(newFile.encryptionKeyHash, file.encryptionKeyHash); + + assert.strictEqual( + reqOpts.headers!['x-goog-copy-source-encryption-key'], + file.encryptionKeyBase64 + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-algorithm'], + 'AES256' + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key'], + file.encryptionKeyBase64 + ); + assert.strictEqual( + reqOpts.headers!['x-goog-encryption-key-sha256'], + file.encryptionKeyHash + ); done(); }; @@ -4660,6 +4781,83 @@ describe('File', () => { file.rotateEncryptionKey({}, done); }); + + it('should update encryption key on success', done => { + const oldKey = 'old-key'; + const newKey = 'new-key'; + file.setEncryptionKey(oldKey); + + const newFile = {}; + file.bucket.file = () => { + return newFile; + }; + + file.copy = ( + destination: string, + options: object, + callback: Function + ) => { + callback(); + }; + + file.rotateEncryptionKey(newKey, (err: any) => { + assert.ifError(err); + assert.strictEqual(file.encryptionKey, newKey); + done(); + }); + }); + + it('should update KMS key on success', done => { + const oldKey = 'old-key'; + const kmsKeyName = 'kms-key'; + file.setEncryptionKey(oldKey); + + const newFile = {}; + file.bucket.file = () => { + return newFile; + }; + + file.copy = ( + destination: string, + options: object, + callback: Function + ) => { + callback(); + }; + + file.rotateEncryptionKey({kmsKeyName}, (err: any) => { + assert.ifError(err); + assert.strictEqual(file.encryptionKey, null); + assert.strictEqual(file.kmsKeyName, kmsKeyName); + done(); + }); + }); + + it('should not update encryption key on failure', done => { + const oldKey = 'old-key'; + const newKey = 'new-key'; + file.setEncryptionKey(oldKey); + + const newFile = {}; + file.bucket.file = () => { + return newFile; + }; + + const copyError = new Error('Copy failed'); + file.copy = ( + destination: string, + options: object, + callback: Function + ) => { + callback(copyError); + }; + + file.rotateEncryptionKey(newKey, (err: any) => { + assert.strictEqual(err, copyError); + assert.strictEqual(file.encryptionKey, oldKey); + done(); + }); + }); }); describe('save', () => { @@ -5459,6 +5657,30 @@ describe('File', () => { done(); }); + + describe('null key', () => { + beforeEach(() => { + file.setEncryptionKey(KEY); + file.setEncryptionKey(null); + }); + + it('should localize the key to null', () => { + assert.strictEqual(file.encryptionKey, null); + }); + + it('should clear the base64 key', () => { + assert.strictEqual(file.encryptionKeyBase64, undefined); + }); + + it('should clear the hash', () => { + assert.strictEqual(file.encryptionKeyHash, undefined); + }); + + it('should remove the request interceptor', () => { + assert.strictEqual(file.encryptionKeyInterceptor, undefined); + assert.strictEqual(file.interceptors.length, 0); + }); + }); }); describe('startResumableUpload_', () => { diff --git a/librarian.yaml b/librarian.yaml index d4d29c13e820..f55632ab1c08 100644 --- a/librarian.yaml +++ b/librarian.yaml @@ -743,7 +743,7 @@ libraries: nodejs: default_version: v1 - name: google-cloud-databasecenter - version: 0.4.0 + version: 0.4.1 apis: - path: google/cloud/databasecenter/v1beta copyright_year: "2026" diff --git a/packages/google-cloud-databasecenter/CHANGELOG.md b/packages/google-cloud-databasecenter/CHANGELOG.md index 1a55e239d67a..366f8547a3bc 100644 --- a/packages/google-cloud-databasecenter/CHANGELOG.md +++ b/packages/google-cloud-databasecenter/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.4.1](https://github.com/googleapis/google-cloud-node/compare/databasecenter-v0.4.0...databasecenter-v0.4.1) (2026-08-05) + + +### Bug Fixes + +* Upgrade types sinon for databasecenter ([#9098](https://github.com/googleapis/google-cloud-node/issues/9098)) ([23aee11](https://github.com/googleapis/google-cloud-node/commit/23aee114a947a29f31977aab8de9c1c7ecd62188)) + ## [0.4.0](https://github.com/googleapis/google-cloud-node/compare/databasecenter-v0.3.0...databasecenter-v0.4.0) (2026-08-04) diff --git a/packages/google-cloud-databasecenter/package.json b/packages/google-cloud-databasecenter/package.json index 61bfb2f90cde..fd525383d2c5 100644 --- a/packages/google-cloud-databasecenter/package.json +++ b/packages/google-cloud-databasecenter/package.json @@ -1,6 +1,6 @@ { "name": "@google-cloud/databasecenter", - "version": "0.4.0", + "version": "0.4.1", "description": "Database Center API client for Node.js", "repository": { "type": "git",