Skip to content

feat(storage): add backend-agnostic conditional-copy support to CopyOptions - #1265

Open
tripodsan wants to merge 1 commit into
mainfrom
storage-copy-conditions
Open

feat(storage): add backend-agnostic conditional-copy support to CopyOptions#1265
tripodsan wants to merge 1 commit into
mainfrom
storage-copy-conditions

Conversation

@tripodsan

Copy link
Copy Markdown
Contributor

Summary

  • Adds ifMatch/ifNoneMatch (destination) and sourceIfMatch (source) as normalized, backend-agnostic CopyOptions fields, following the same pattern already established by metadataDirective.
  • Closes a real gap surfaced while migrating helix-azure-api-service off S3: conditional-copy predicates could previously only be expressed via the untyped, backend-native copyOpts escape hatch (e.g. S3's IfMatch/IfNoneMatch/CopySourceIfMatch), so optimistic-concurrency copy logic built against that shape silently became a no-op once a caller switched to StorageAzure (Azure's SDK expects a completely different conditions/sourceConditions shape).
  • S3Backend#copy() maps the new fields onto CopyObjectCommand's native IfMatch/IfNoneMatch/CopySourceIfMatch.
  • AzureBackend#copy() maps them onto beginCopyFromURL's conditions/sourceConditions.
  • Both backends' copy() now normalize any thrown error's native HTTP status onto e.status (previously only the "source missing" 404 case was normalized), so callers can branch on precondition failures (412/409) without knowing the backend's native error shape.

Why

helix-azure-api-service's source-client.js implements optimistic-create ("copy only if destination doesn't exist") and optimistic-update/versioning ("copy only if destination/source etag still matches") on top of Bucket#copy(), using raw AWS-SDK-v3 field names passed through copyOpts. This worked against the old S3-only backend, but is inert against StorageAzure. Rather than teaching application code about each backend's native option shape, this extends the storage abstraction itself — the same way metadataDirective: 'COPY'|'REPLACE' already abstracts over S3's MetadataDirective vs. Azure's separate setHTTPHeaders() call.

Test plan

  • npm test in helix-shared-storage, helix-shared-storage-s3, helix-shared-storage-azure — all at 100% coverage (existing threshold), new tests added for: destination ifMatch/ifNoneMatch mapping, source sourceIfMatch mapping, and non-404 error-status normalization, for both backends.
  • npm test --workspaces from the repo root — all packages pass, no regressions.
  • npm run lint in each touched package — clean.

Follow-up (separate, out of scope here): once this is released, helix-azure-api-service's source-client.js will be updated to use the new normalized fields instead of its current raw copyOpts passthrough, and its currently-skipped copy-semantics tests will be un-skipped against real Azure behavior.

🤖 Generated with Claude Code

…ptions

Adds ifMatch/ifNoneMatch (destination) and sourceIfMatch (source) as
normalized CopyOptions fields, following the same pattern as the
existing metadataDirective abstraction. Previously, conditional-copy
predicates could only be expressed via the untyped, backend-native
copyOpts escape hatch (e.g. S3's IfMatch/IfNoneMatch/CopySourceIfMatch),
which meant callers relying on optimistic-concurrency semantics for
copy operations only worked against S3 and were silent no-ops against
Azure.

- S3Backend maps the new fields onto CopyObjectCommand's native
  IfMatch/IfNoneMatch/CopySourceIfMatch fields.
- AzureBackend maps them onto beginCopyFromURL's conditions/
  sourceConditions shape.
- Both backends' copy() now normalize any thrown error's native HTTP
  status onto e.status (previously only the 404 case was normalized),
  so callers can branch on precondition failures (412/409) without
  knowing the backend's native error shape.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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 participant