Skip to content

fix(bulk-operations): chunk CS Assets bulk delete/move to the 100-item API cap (DX-9732) - #297

Merged
naman-contentstack merged 2 commits into
v2-devfrom
fix/DX-9732
Jul 28, 2026
Merged

fix(bulk-operations): chunk CS Assets bulk delete/move to the 100-item API cap (DX-9732)#297
naman-contentstack merged 2 commits into
v2-devfrom
fix/DX-9732

Conversation

@naman-contentstack

@naman-contentstack naman-contentstack commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Problem

DX-9732: cm:stacks:bulk-assets --operation delete fails with HTTP 422 when the asset-uids file has >100 entries:

CS Assets API POST failed: status 422 ... {"errors":["Assets cannot exceed the max limit of 100."],"error_code":422}

The adapter sent the entire asset array in one POST. The CS Assets bulk delete/move endpoints cap each request at 100 items, so any bulk delete over 100 assets was completely blocked with no CLI workaround. bulkMoveAssets had the same shape.

Fix

Chunk to the cap and dispatch the batches — the publish-side QueueManager was the wrong tool (SDK/CMS-auth coupled; the delete path deliberately skips that pipeline), so this reuses the asset-management adapter's own concurrency primitives.

  • Chunking (adapter). CSAssetsAdapter.bulkDeleteAssets/bulkMoveAssets split the payload into ≤100-item batches (CS_ASSETS_BULK_MUTATE_MAX_ITEMS) via a shared dispatchBulkMutateBatches, dispatched with bounded concurrency (makeConcurrentCall) and per-batch retry (postJson({retry:true}) — 429/5xx retried, 4xx like the 422 cap not).
  • Continue on partial failure. Failed batches are collected, not rethrown (each request commits independently server-side). The aggregate result carries job_ids, notices, batchesTotal, batchesSucceeded, and failures[] ({batchIndex, count, status, error, uids}).
  • Typed errors. postJson throws CsAssetsPostError carrying the HTTP status — no error-string parsing.
  • Partial-failure UX. The runner reports what committed vs failed, writes the failed uids to a {"uids":[...]} file in the bulk-operation log folder, and prints a re-run hint (--asset-uids-file). No --retry-failed (CMS-only). Delete messaging states a submitted job is submission, not completion (delete is async).

Testing

  • contentstack-asset-management: 245 passing — adapter chunking (250→3 batches ≤100), partial-failure aggregation, failed-uid capture.
  • contentstack-bulk-operations: 831 passing — service aggregate mapping, runner partial-failure file write.
  • Verified end-to-end against a 628-uid file: 7 batches of ≤100, no 422.

🤖 Generated with Claude Code

…m API cap (DX-9732)

CS Assets bulk delete/move sent the entire asset array in one POST, so any
request over 100 items failed with HTTP 422 "Assets cannot exceed the max
limit of 100." — blocking bulk deletes of >100 assets entirely.

- CSAssetsAdapter.bulkDeleteAssets/bulkMoveAssets now split the payload into
  <=100-item batches (CS_ASSETS_BULK_MUTATE_MAX_ITEMS) dispatched with bounded
  concurrency (makeConcurrentCall) and per-batch retry (429/5xx; 4xx not retried).
- Continue-on-partial-failure: failed batches are collected, not rethrown, since
  each request commits independently. Aggregate carries job_ids, batch counts,
  and per-batch failures (with the failed uids).
- postJson throws a typed CsAssetsPostError carrying HTTP status (no error-string
  parsing).
- Runner reports partial outcomes, writes failed uids to a {"uids":[...]} file in
  the bulk-operation log folder for re-run via --asset-uids-file, and states that
  a submitted delete job is submission (not completion) since delete is async.

Tests: adapter chunking/partial-failure/uid-capture; runner partial-failure file write.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@naman-contentstack
naman-contentstack requested a review from a team as a code owner July 17, 2026 12:22
@naman-contentstack naman-contentstack self-assigned this Jul 17, 2026
@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 18 200 25 ✅ Passed
🟡 Medium Severity 2 2 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 1 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 200
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

@naman-contentstack
naman-contentstack merged commit 27dcc22 into v2-dev Jul 28, 2026
9 of 11 checks passed
@naman-contentstack
naman-contentstack deleted the fix/DX-9732 branch July 28, 2026 12:01
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.

2 participants