Skip to content

feat: support intra-bucket copy/move in GCP Storage Bucket binding#4410

Draft
nelson-parente wants to merge 4 commits into
dapr:mainfrom
nelson-parente:feat/gcp-bucket-intra-bucket-copy-move
Draft

feat: support intra-bucket copy/move in GCP Storage Bucket binding#4410
nelson-parente wants to merge 4 commits into
dapr:mainfrom
nelson-parente:feat/gcp-bucket-intra-bucket-copy-move

Conversation

@nelson-parente

@nelson-parente nelson-parente commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Today the GCP Storage Bucket binding's copy and move operations require destinationBucket and hardcode the destination key to the source key. This makes it impossible to copy/move an object between key prefixes within the same bucket (e.g. public/file.xlsxadhoc/file.xlsx) without a costly get+create round-trip that also loses content-type, custom metadata, and ACLs.

Changes

  • Added optional destinationKey field to the copy and move operation data payloads. When omitted it defaults to the source key.
  • Made destinationBucket optional in both operations. When omitted it defaults to the component's configured bucket.
  • Both fields may be supplied independently or together.
  • Reject only the case where both destinationBucket and destinationKey are omitted — that would produce a no-op copy/move onto itself. A clear error is returned in that case.
  • All copies continue to use the GCS server-side CopierFrom API, so content-type, custom metadata, and ACLs are preserved without streaming bytes through the app/sidecar.

Example — intra-bucket prefix change (new)

{ "operation": "copy", "metadata": { "key": "public/file.xlsx" }, "data": { "destinationKey": "adhoc/file.xlsx" } }
{ "operation": "move", "metadata": { "key": "public/file.xlsx" }, "data": { "destinationKey": "adhoc/file.xlsx" } }

Example — cross-bucket, different key (unchanged behaviour still works)

{ "operation": "copy", "metadata": { "key": "reports/q1.pdf" }, "data": { "destinationBucket": "archive-bucket", "destinationKey": "2024/q1.pdf" } }

Validation matrix

destinationBucket destinationKey Result
omitted omitted Error — source and destination are identical
omitted provided Intra-bucket copy/move to new key ✓
provided omitted Cross-bucket copy/move, same key ✓ (existing behaviour)
provided provided Cross-bucket copy/move to new key ✓

Tests

Unit tests added/updated in bucket_test.go:

  • Decode destinationKey from payload
  • Verify defaults when only one field is provided
  • Error on both-omitted (no-op) case for copy and move
  • All existing tests updated to reflect the relaxed requirement (37 tests pass, no live GCS needed)

Conformance tests that require a live GCS connection are not affected and must be run separately.

Fixes #4377


📖 Documentation: dapr/docs#5218

Add optional `destinationKey` to copy/move operation payloads.
Make `destinationBucket` optional, defaulting to the component's
configured bucket when omitted. Reject only the case where both
fields are absent (that would be a no-op onto itself). Preserves
GCS server-side copy semantics so content-type, metadata, and ACLs
are retained without streaming bytes through the sidecar.

Fixes dapr#4377

Signed-off-by: Nelson Parente <nelson@diagrid.io>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the GCP Storage Bucket output binding to support intra-bucket copy/move across key prefixes by allowing callers to optionally specify a destinationKey, and by making destinationBucket optional (defaulting to the component’s configured bucket).

Changes:

  • Added destinationKey to copy and move payloads and centralized destination resolution/defaulting in resolveCopyDestination.
  • Relaxed destinationBucket requirement by defaulting it to the configured bucket when omitted.
  • Added/updated unit tests to cover the destination resolution matrix and updated error assertions for wrapped JSON unmarshal errors.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
bindings/gcp/bucket/metadata.yaml Documents the new copy/move behavior (needs wording alignment with actual validation).
bindings/gcp/bucket/bucket.go Implements destination defaulting for copy/move and uses resolved bucket/key for the GCS CopierFrom operation.
bindings/gcp/bucket/bucket_test.go Adds unit tests for destination resolution and adjusts copy/move validation expectations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/metadata.yaml Outdated
Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/metadata.yaml Outdated
…ve docs (Copilot review)

Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

… behavior

Reword the move operation metadata.yaml description to state that the
resolved destination (after defaults are applied) must not equal the source,
matching the actual guard in move(). Add a comment in copy() documenting that
copy-onto-self is intentionally allowed (GCS creates a new object generation),
unlike move. Addresses Copilot review feedback.

Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/metadata.yaml
The both-omitted validation message described the resolved source==destination
case as a no-op for copy, which contradicts the copy() comment noting that
copy-onto-self is a valid GCS operation (it rewrites the object as a new
generation). Reword the error and the copy operation description to say it
resolves to the source object (rewritten for copy, deleted for move).
Addresses Copilot review feedback.

Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
@nelson-parente nelson-parente requested a review from Copilot June 23, 2026 11:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/bucket.go
Comment thread bindings/gcp/bucket/bucket.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[1.19] GCP Storage Bucket binding - enabling intra-bucket copy/move between key prefixes.

2 participants